<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>WebGPU on afloat.boats</title><link>https://www.afloat.boats/tags/webgpu/</link><description>Recent content in WebGPU on afloat.boats</description><generator>Hugo -- gohugo.io</generator><language>en-us</language><lastBuildDate>Thu, 26 Mar 2026 21:11:57 -0700</lastBuildDate><atom:link href="https://www.afloat.boats/tags/webgpu/index.xml" rel="self" type="application/rss+xml"/><item><title>Whither GPU? - Demystifying Matrices</title><link>https://www.afloat.boats/posts/demystifying-matrices/</link><pubDate>Thu, 26 Mar 2026 21:11:57 -0700</pubDate><guid>https://www.afloat.boats/posts/demystifying-matrices/</guid><description>Building intuition for matrix transforms by building a rotating cube with Canvas2D APIs.</description><content:encoded><![CDATA[<p>On Christmas last year, I came across a <a href="https://www.youtube.com/watch?v=qjWkNZ0SXfo">video</a> by Tsoding. Now when I say that he has a gift for distilling complex and sometimes even scary topics in a way that completely makes sense, I&rsquo;m not exaggerating. In this particular video, he quite literally demystifies the illusion of 3d graphics created on a flat 2d screen.</p>
<p>I spent some time building an extension to his project <a href="https://github.com/tauseefk/mnky">here</a>, an attempt to demystify rendering on the GPU, by way of matrices.</p>
<p><a href="/mnky" target="_blank" rel="noopener noreferrer">MNKY</a> | <a href="https://github.com/tauseefk/mnky" target="_blank" rel="noopener noreferrer">Repository</a></p>
<p>In this post, we&rsquo;ll build a Canvas2d renderer to display a rotating cube, and then build intuition towards using matrices for linear transformations (like rotation).</p>
<p>Those who have already watched the video, can skim all the way to <a href="#matrices">Matrices</a>.</p>
<h4 id="drawing-a-cube">Drawing a Cube</h4>
<p>For simplicity, I will start with rendering using Canvas 2D APIs, and slowly move to the GPU. It will allow for seamlessly switching between the two rendering pipelines and seeing the difference.</p>
<p>There&rsquo;s a bit of boilerplate in the repo, which you don&rsquo;t necessarily need, however using TypeScript and a bunch of tools (like Biome) make the experience much more pleasant.</p>
<h5 id="instantiating-canvas2d">Instantiating Canvas2D</h5>
<p>This requires creating an HTML canvas element, and using JavaScript to grab a handle to it. If you haven&rsquo;t watched Tsoding&rsquo;s video, he shows some neat tricks like accessing elements by id directly on the global object, however we&rsquo;re going to do it the more verbose way.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-typescript" data-lang="typescript"><span class="line"><span class="cl"><span class="c1">// src/canvas.ts
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">function</span> <span class="nx">setupCanvas2d</span><span class="p">(</span><span class="nx">element</span>: <span class="kt">HTMLCanvasElement</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="c1">// this wrapper will make sense later when we draw per frame
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="kr">const</span> <span class="nx">init</span> <span class="o">=</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">element</span><span class="p">.</span><span class="nx">width</span> <span class="o">=</span> <span class="nx">CANVAS_SIZE</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="nx">element</span><span class="p">.</span><span class="nx">height</span> <span class="o">=</span> <span class="nx">CANVAS_SIZE</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// 1. handle to the canvas 2d context
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="kr">const</span> <span class="nx">context2d</span> <span class="o">=</span> <span class="nx">element</span><span class="p">.</span><span class="nx">getContext</span><span class="p">(</span><span class="s2">&#34;2d&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">context2d</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="k">throw</span> <span class="k">new</span> <span class="nb">Error</span><span class="p">(</span><span class="s2">&#34;Failed to get drawing context&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// 2. set fill color for shapes
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nx">context2d</span><span class="p">.</span><span class="nx">fillStyle</span> <span class="o">=</span> <span class="nx">CLEAR_COLOR</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 3. sets the entire canvas color to CLEAR_COLOR
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nx">context2d</span><span class="p">.</span><span class="nx">fillRect</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="nx">CANVAS_SIZE</span><span class="p">,</span> <span class="nx">CANVAS_SIZE</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nx">context2d</span><span class="p">.</span><span class="nx">fillStyle</span> <span class="o">=</span> <span class="nx">COLOR</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 4. rectangle spans from (x1, y1) to (x2, y2) coordinates
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nx">context2d</span><span class="p">.</span><span class="nx">rect</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="nx">RECT_SIZE</span><span class="p">,</span> <span class="nx">RECT_SIZE</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="nx">context2d</span><span class="p">.</span><span class="nx">fill</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">  <span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nx">init</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><pre tabindex="0"><code>(-1.0, -1.0)               ( 1.0, -1.0)
     ┌──────────────────────────┐
     │                          │
     │                          │
     │                          │
     │                          │
     │                          │
     │       CLEAR_COLOR        │
     │                          │
     │                          │
     │                          │
     │                          │
     │                          │
     │                          │
     └──────────────────────────┘
(-1.0,  1.0)               ( 1.0,  1.0)
</code></pre><ol>
<li>The handle provides access to the actual drawing APIs.</li>
<li>Canvas2D APIs are imperative, which means that you set some config, and all subsequent API calls use the updated config values. In this case the following <code>fillRect</code> call will fill the rect with the <code>CLEAR_COLOR</code>.</li>
<li>This fills the entire canavas to a dark gray color, which gives me a decent enough starting point to continue drawing shapes on.</li>
<li>Finally draw a rectangle of width x2 - x1, and height y2 - y1. (0, 0) is the top left of the canvas, and positive values move towards the right and down. We&rsquo;re going to use this rectangle as the background.</li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-typescript" data-lang="typescript"><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">root</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">querySelector</span><span class="p">&lt;</span><span class="nt">HTMLDivElement</span><span class="p">&gt;(</span><span class="s2">&#34;#app&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">root</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="k">throw</span> <span class="k">new</span> <span class="nb">Error</span><span class="p">(</span><span class="s2">&#34;No root to bind&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 1. attach the canvas to root
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">root</span><span class="p">.</span><span class="nx">innerHTML</span> <span class="o">=</span> <span class="sb">`
</span></span></span><span class="line"><span class="cl"><span class="sb">  &lt;div&gt;
</span></span></span><span class="line"><span class="cl"><span class="sb">    &lt;div class=&#34;card&#34;&gt;
</span></span></span><span class="line"><span class="cl"><span class="sb">      &lt;canvas id=&#34;canvas-2d&#34; /&gt;
</span></span></span><span class="line"><span class="cl"><span class="sb">    &lt;/div&gt;
</span></span></span><span class="line"><span class="cl"><span class="sb">  &lt;/div&gt;
</span></span></span><span class="line"><span class="cl"><span class="sb">`</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">canvas2d</span>: <span class="kt">HTMLCanvasElement</span> <span class="o">|</span> <span class="kc">null</span> <span class="o">=</span> <span class="nb">document</span><span class="p">.</span><span class="nx">querySelector</span><span class="p">(</span><span class="s2">&#34;#canvas-2d&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">canvas2d</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="k">throw</span> <span class="k">new</span> <span class="nb">Error</span><span class="p">(</span><span class="s2">&#34;No root to bind&#34;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 2. call setupCanvas2d from the previous section
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="nx">setupCanvas2d</span><span class="p">(</span><span class="nx">canvas2d</span><span class="p">);</span>
</span></span></code></pre></div><ol>
<li>Attach a canvas element to the root, so we can draw things to it.</li>
<li>Call the setup function, which in turn draws the rectangle with <code>CLEAR_COLOR</code>.</li>
</ol>
<h5 id="making-a-point">Making a point</h5>
<p>Now that drawing a background is out of the way, let&rsquo;s try drawing some actual points.
As we&rsquo;re going to move to WebGPU rendering pipeline pretty soon, we&rsquo;ll create a mapping between the coordinate spaces used by Canvas2D and WebGPU for easy translation. This allows us to store points in a standardized form, used by portable formats like Obj. We&rsquo;ll start with drawing a single point and move on to drawing quads, and finally a cube.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-typescript" data-lang="typescript"><span class="line"><span class="cl"><span class="c1">// src/canvas.ts
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="kr">type</span> <span class="nx">Dimensions</span> <span class="o">=</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">width</span>: <span class="kt">number</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="nx">height</span>: <span class="kt">number</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kr">interface</span> <span class="nx">Point2D</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">x</span>: <span class="kt">number</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">  <span class="nx">y</span>: <span class="kt">number</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 1. NDC to screen space coordinates
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kr">export</span> <span class="kr">const</span> <span class="nx">screen</span> <span class="o">=</span> <span class="p">(</span>
</span></span><span class="line"><span class="cl">  <span class="p">{</span> <span class="nx">x</span><span class="p">,</span> <span class="nx">y</span> <span class="p">}</span><span class="o">:</span> <span class="nx">Point2D</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nx">screenDimensions</span>: <span class="kt">Dimensions</span><span class="p">,</span>
</span></span><span class="line"><span class="cl"><span class="p">)</span><span class="o">:</span> <span class="nx">Point2D</span> <span class="o">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">x</span><span class="o">:</span> <span class="p">((</span><span class="nx">x</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span> <span class="o">/</span> <span class="mi">2</span><span class="p">)</span> <span class="o">*</span> <span class="nx">screenDimensions</span><span class="p">.</span><span class="nx">width</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 2. y-flip
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nx">y</span><span class="o">:</span> <span class="p">(</span><span class="mi">1</span> <span class="o">-</span> <span class="p">(</span><span class="nx">y</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span> <span class="o">/</span> <span class="mi">2</span><span class="p">)</span> <span class="o">*</span> <span class="nx">screenDimensions</span><span class="p">.</span><span class="nx">height</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="p">};</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span></code></pre></div><ol>
<li>WebGPU uses [-1, -1] to [1, 1] as it&rsquo;s coordinate space. We&rsquo;ll use the same normalized coordinates when issuing draw commands to Canvas2D API to keep things consistent.
The types Dimensions and Point2D are just for readability, and encoding intent into the code.</li>
<li>Y-axis is flipped, as WebGPU coordinates increase from top to bottom, as opposed to that of the Canvas2D API.</li>
</ol>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-typescript" data-lang="typescript"><span class="line"><span class="cl"><span class="c1">// src/canvas.ts
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">function</span> <span class="nx">setupCanvas2d</span><span class="p">(</span><span class="nx">element</span>: <span class="kt">HTMLCanvasElement</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="kr">const</span> <span class="nx">init</span> <span class="o">=</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="p">...</span>
</span></span><span class="line"><span class="cl">    <span class="nx">context2d</span><span class="p">.</span><span class="nx">fillStyle</span> <span class="o">=</span> <span class="nx">COLOR</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// 1. first point&#39;s coordinates converted to screen space
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="o">+</span>   <span class="kr">const</span> <span class="nx">point1</span> <span class="o">=</span> <span class="nx">screen</span><span class="p">({</span> <span class="nx">x</span><span class="o">:</span> <span class="o">-</span><span class="mf">0.5</span><span class="p">,</span> <span class="nx">y</span>: <span class="kt">0.5</span> <span class="p">},</span> <span class="nx">SCREEN_DIMENSIONS</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// 2. draw a square at the position
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="o">+</span>   <span class="nx">context2d</span><span class="p">.</span><span class="nx">rect</span><span class="p">(</span><span class="nx">point1</span><span class="p">.</span><span class="nx">x</span><span class="p">,</span> <span class="nx">point1</span><span class="p">.</span><span class="nx">y</span><span class="p">,</span> <span class="nx">POINT_SIZE</span><span class="p">,</span> <span class="nx">POINT_SIZE</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">...</span>
</span></span><span class="line"><span class="cl">  <span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nx">init</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><pre tabindex="0"><code>(-1.0, -1.0)               ( 1.0, -1.0)
     ┌──────────────────────────┐
     │                          │
     │                          │
     │                          │
     │                          │
     │                          │
     │                          │
     │                          │
     │                          │
     │                          │
     │     •                    │
     │(-0.5,  0.5)              │
     │                          │
     └──────────────────────────┘
(-1.0,  1.0)               ( 1.0,  1.0)
</code></pre><ol>
<li><code>[-0.5, 0.5]</code> is the lower left corner of a square of side 1.0 centered at <code>[0, 0]</code>. Which gets converted to screen space coordinates.</li>
<li>There&rsquo;s no reason to draw the point as a square, other than that it&rsquo;s convenient.</li>
</ol>
<h5 id="drawing-a-quad">Drawing a quad</h5>
<p>Now let&rsquo;s see if you can deduce the coordinates for the other three points.</p>
<pre tabindex="0"><code>(-1.0, -1.0)               ( 1.0, -1.0)
     ┌──────────────────────────┐
     │                          │
     │     •             •      │
     │                          │
     │                          │
     │                          │
     │                          │
     │                          │
     │                          │
     │                          │
     │     •             •      │
     │                          │
     └──────────────────────────┘
(-1.0,  1.0)               ( 1.0,  1.0)
</code></pre><p>Drawing these points is quite straight now that we just have to repeat the previous step with different coordinates.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-typescript" data-lang="typescript"><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">setupCanvas2d</span><span class="p">(</span><span class="nx">element</span>: <span class="kt">HTMLCanvasElement</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="kr">const</span> <span class="nx">init</span> <span class="o">=</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="p">...</span>
</span></span><span class="line"><span class="cl">    <span class="kr">const</span> <span class="nx">point1</span> <span class="o">=</span> <span class="nx">screen</span><span class="p">({</span> <span class="nx">x</span><span class="o">:</span> <span class="o">-</span><span class="mf">0.5</span><span class="p">,</span> <span class="nx">y</span>: <span class="kt">0.5</span> <span class="p">},</span> <span class="nx">SCREEN_DIMENSIONS</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 1. coordinates for the other three points
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="o">+</span>   <span class="kr">const</span> <span class="nx">point2</span> <span class="o">=</span> <span class="nx">screen</span><span class="p">({</span> <span class="nx">x</span>: <span class="kt">0.5</span><span class="p">,</span> <span class="nx">y</span>: <span class="kt">0.5</span> <span class="p">},</span> <span class="nx">SCREEN_DIMENSIONS</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="o">+</span>   <span class="kr">const</span> <span class="nx">point3</span> <span class="o">=</span> <span class="nx">screen</span><span class="p">({</span> <span class="nx">x</span>: <span class="kt">0.5</span><span class="p">,</span> <span class="nx">y</span><span class="o">:</span> <span class="o">-</span><span class="mf">0.5</span> <span class="p">},</span> <span class="nx">SCREEN_DIMENSIONS</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="o">+</span>   <span class="kr">const</span> <span class="nx">point4</span> <span class="o">=</span> <span class="nx">screen</span><span class="p">({</span> <span class="nx">x</span><span class="o">:</span> <span class="o">-</span><span class="mf">0.5</span><span class="p">,</span> <span class="nx">y</span><span class="o">:</span> <span class="o">-</span><span class="mf">0.5</span> <span class="p">},</span> <span class="nx">SCREEN_DIMENSIONS</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nx">context2d</span><span class="p">.</span><span class="nx">rect</span><span class="p">(</span><span class="nx">point1</span><span class="p">.</span><span class="nx">x</span><span class="p">,</span> <span class="nx">point1</span><span class="p">.</span><span class="nx">y</span><span class="p">,</span> <span class="nx">POINT_SIZE</span><span class="p">,</span> <span class="nx">POINT_SIZE</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 2. draw the new points
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="o">+</span>   <span class="nx">context2d</span><span class="p">.</span><span class="nx">rect</span><span class="p">(</span><span class="nx">point2</span><span class="p">.</span><span class="nx">x</span><span class="p">,</span> <span class="nx">point2</span><span class="p">.</span><span class="nx">y</span><span class="p">,</span> <span class="nx">POINT_SIZE</span><span class="p">,</span> <span class="nx">POINT_SIZE</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="o">+</span>   <span class="nx">context2d</span><span class="p">.</span><span class="nx">rect</span><span class="p">(</span><span class="nx">point3</span><span class="p">.</span><span class="nx">x</span><span class="p">,</span> <span class="nx">point3</span><span class="p">.</span><span class="nx">y</span><span class="p">,</span> <span class="nx">POINT_SIZE</span><span class="p">,</span> <span class="nx">POINT_SIZE</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="o">+</span>   <span class="nx">context2d</span><span class="p">.</span><span class="nx">rect</span><span class="p">(</span><span class="nx">point4</span><span class="p">.</span><span class="nx">x</span><span class="p">,</span> <span class="nx">point4</span><span class="p">.</span><span class="nx">y</span><span class="p">,</span> <span class="nx">POINT_SIZE</span><span class="p">,</span> <span class="nx">POINT_SIZE</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">...</span>
</span></span><span class="line"><span class="cl">  <span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nx">init</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h5 id="just-projecting">Just projecting</h5>
<p>Let&rsquo;s draw another set of points, but a little farther into the screen.
So far, we don&rsquo;t have a concept of depth, so let&rsquo;s add another dimension to our points. And add a way to project 3d points onto a 2d surface.
This is how the illusion of depth is created on a flat 2d-screen, make some things smaller in comparison to others, and our brain magically thinks something is far away.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-typescript" data-lang="typescript"><span class="line"><span class="cl"><span class="c1">// src/canvas.ts
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kr">interface</span> <span class="nx">Point3D</span> <span class="kr">extends</span> <span class="nx">Point2D</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">z</span>: <span class="kt">number</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 1. x and y scaled down in proportion to depth
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kr">const</span> <span class="nx">project</span> <span class="o">=</span> <span class="p">({</span> <span class="nx">x</span><span class="p">,</span> <span class="nx">y</span><span class="p">,</span> <span class="nx">z</span> <span class="p">}</span><span class="o">:</span> <span class="nx">Point3D</span><span class="p">)</span><span class="o">:</span> <span class="nx">Point2D</span> <span class="o">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="p">{</span> <span class="nx">x</span>: <span class="kt">x</span> <span class="o">/</span> <span class="nx">z</span><span class="p">,</span> <span class="nx">y</span>: <span class="kt">y</span> <span class="o">/</span> <span class="nx">z</span> <span class="p">};</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">setupCanvas2d</span><span class="p">(</span><span class="nx">element</span>: <span class="kt">HTMLCanvasElement</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="kr">const</span> <span class="nx">init</span> <span class="o">=</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="p">...</span>
</span></span><span class="line"><span class="cl"><span class="o">-</span>   <span class="kr">const</span> <span class="nx">point1</span> <span class="o">=</span> <span class="nx">screen</span><span class="p">({</span> <span class="nx">x</span><span class="o">:</span> <span class="o">-</span><span class="mf">0.5</span><span class="p">,</span> <span class="nx">y</span>:  <span class="kt">0.5</span> <span class="p">},</span> <span class="nx">SCREEN_DIMENSIONS</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="o">-</span>   <span class="kr">const</span> <span class="nx">point2</span> <span class="o">=</span> <span class="nx">screen</span><span class="p">({</span> <span class="nx">x</span>:  <span class="kt">0.5</span><span class="p">,</span> <span class="nx">y</span>:  <span class="kt">0.5</span> <span class="p">},</span> <span class="nx">SCREEN_DIMENSIONS</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="o">-</span>   <span class="kr">const</span> <span class="nx">point3</span> <span class="o">=</span> <span class="nx">screen</span><span class="p">({</span> <span class="nx">x</span>:  <span class="kt">0.5</span><span class="p">,</span> <span class="nx">y</span><span class="o">:</span> <span class="o">-</span><span class="mf">0.5</span> <span class="p">},</span> <span class="nx">SCREEN_DIMENSIONS</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="o">-</span>   <span class="kr">const</span> <span class="nx">point4</span> <span class="o">=</span> <span class="nx">screen</span><span class="p">({</span> <span class="nx">x</span><span class="o">:</span> <span class="o">-</span><span class="mf">0.5</span><span class="p">,</span> <span class="nx">y</span><span class="o">:</span> <span class="o">-</span><span class="mf">0.5</span> <span class="p">},</span> <span class="nx">SCREEN_DIMENSIONS</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// project existing coordinates
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="o">+</span>   <span class="kr">const</span> <span class="nx">point1</span> <span class="o">=</span> <span class="nx">screen</span><span class="p">(</span><span class="nx">project</span><span class="p">({</span> <span class="nx">x</span><span class="o">:</span> <span class="o">-</span><span class="mf">0.5</span><span class="p">,</span> <span class="nx">y</span>:  <span class="kt">0.5</span><span class="p">,</span> <span class="nx">z</span>: <span class="kt">1.0</span> <span class="p">}),</span> <span class="nx">SCREEN_DIMENSIONS</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="o">+</span>   <span class="kr">const</span> <span class="nx">point2</span> <span class="o">=</span> <span class="nx">screen</span><span class="p">(</span><span class="nx">project</span><span class="p">({</span> <span class="nx">x</span>:  <span class="kt">0.5</span><span class="p">,</span> <span class="nx">y</span>:  <span class="kt">0.5</span><span class="p">,</span> <span class="nx">z</span>: <span class="kt">1.0</span> <span class="p">}),</span> <span class="nx">SCREEN_DIMENSIONS</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="o">+</span>   <span class="kr">const</span> <span class="nx">point3</span> <span class="o">=</span> <span class="nx">screen</span><span class="p">(</span><span class="nx">project</span><span class="p">({</span> <span class="nx">x</span>:  <span class="kt">0.5</span><span class="p">,</span> <span class="nx">y</span><span class="o">:</span> <span class="o">-</span><span class="mf">0.5</span><span class="p">,</span> <span class="nx">z</span>: <span class="kt">1.0</span>  <span class="p">}),</span> <span class="nx">SCREEN_DIMENSIONS</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="o">+</span>   <span class="kr">const</span> <span class="nx">point4</span> <span class="o">=</span> <span class="nx">screen</span><span class="p">(</span><span class="nx">project</span><span class="p">({</span> <span class="nx">x</span><span class="o">:</span> <span class="o">-</span><span class="mf">0.5</span><span class="p">,</span> <span class="nx">y</span><span class="o">:</span> <span class="o">-</span><span class="mf">0.5</span><span class="p">,</span> <span class="nx">z</span>: <span class="kt">1.0</span>  <span class="p">}),</span> <span class="nx">SCREEN_DIMENSIONS</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// add the second set of quad points
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="o">+</span>   <span class="kr">const</span> <span class="nx">point5</span> <span class="o">=</span> <span class="nx">screen</span><span class="p">(</span><span class="nx">project</span><span class="p">({</span> <span class="nx">x</span><span class="o">:</span> <span class="o">-</span><span class="mf">0.5</span><span class="p">,</span> <span class="nx">y</span>:  <span class="kt">0.5</span><span class="p">,</span> <span class="nx">z</span>: <span class="kt">2.0</span> <span class="p">}),</span> <span class="nx">SCREEN_DIMENSIONS</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="o">+</span>   <span class="kr">const</span> <span class="nx">point6</span> <span class="o">=</span> <span class="nx">screen</span><span class="p">(</span><span class="nx">project</span><span class="p">({</span> <span class="nx">x</span>:  <span class="kt">0.5</span><span class="p">,</span> <span class="nx">y</span>:  <span class="kt">0.5</span><span class="p">,</span> <span class="nx">z</span>: <span class="kt">2.0</span> <span class="p">}),</span> <span class="nx">SCREEN_DIMENSIONS</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="o">+</span>   <span class="kr">const</span> <span class="nx">point7</span> <span class="o">=</span> <span class="nx">screen</span><span class="p">(</span><span class="nx">project</span><span class="p">({</span> <span class="nx">x</span>:  <span class="kt">0.5</span><span class="p">,</span> <span class="nx">y</span><span class="o">:</span> <span class="o">-</span><span class="mf">0.5</span><span class="p">,</span> <span class="nx">z</span>: <span class="kt">2.0</span>  <span class="p">}),</span> <span class="nx">SCREEN_DIMENSIONS</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="o">+</span>   <span class="kr">const</span> <span class="nx">point8</span> <span class="o">=</span> <span class="nx">screen</span><span class="p">(</span><span class="nx">project</span><span class="p">({</span> <span class="nx">x</span><span class="o">:</span> <span class="o">-</span><span class="mf">0.5</span><span class="p">,</span> <span class="nx">y</span><span class="o">:</span> <span class="o">-</span><span class="mf">0.5</span><span class="p">,</span> <span class="nx">z</span>: <span class="kt">2.0</span>  <span class="p">}),</span> <span class="nx">SCREEN_DIMENSIONS</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="p">...</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// draw the second set of points after the first set
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="o">+</span>   <span class="nx">context2d</span><span class="p">.</span><span class="nx">rect</span><span class="p">(</span><span class="nx">point5</span><span class="p">.</span><span class="nx">x</span><span class="p">,</span> <span class="nx">point5</span><span class="p">.</span><span class="nx">y</span><span class="p">,</span> <span class="nx">POINT_SIZE</span><span class="p">,</span> <span class="nx">POINT_SIZE</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="o">+</span>   <span class="nx">context2d</span><span class="p">.</span><span class="nx">rect</span><span class="p">(</span><span class="nx">point6</span><span class="p">.</span><span class="nx">x</span><span class="p">,</span> <span class="nx">point6</span><span class="p">.</span><span class="nx">y</span><span class="p">,</span> <span class="nx">POINT_SIZE</span><span class="p">,</span> <span class="nx">POINT_SIZE</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="o">+</span>   <span class="nx">context2d</span><span class="p">.</span><span class="nx">rect</span><span class="p">(</span><span class="nx">point7</span><span class="p">.</span><span class="nx">x</span><span class="p">,</span> <span class="nx">point7</span><span class="p">.</span><span class="nx">y</span><span class="p">,</span> <span class="nx">POINT_SIZE</span><span class="p">,</span> <span class="nx">POINT_SIZE</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="o">+</span>   <span class="nx">context2d</span><span class="p">.</span><span class="nx">rect</span><span class="p">(</span><span class="nx">point8</span><span class="p">.</span><span class="nx">x</span><span class="p">,</span> <span class="nx">point8</span><span class="p">.</span><span class="nx">y</span><span class="p">,</span> <span class="nx">POINT_SIZE</span><span class="p">,</span> <span class="nx">POINT_SIZE</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="p">...</span>
</span></span><span class="line"><span class="cl">  <span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nx">init</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><ol>
<li>This is a simplified projection, makes the farther objects smaller compared to the ones near the observer, in proportion to the depth.</li>
</ol>
<pre tabindex="0"><code>(-1.0, -1.0)               ( 1.0, -1.0)
     ┌──────────────────────────┐
     │                          │
     │   3 •            • 2     │
     │                          │
     │      7 •       • 6       │
     │                          │
     │                          │
     │                          │
     │      4 •       • 5       │
     │                          │
     │   0 •             • 1    │
     │                          │
     └──────────────────────────┘
(-1.0,  1.0)               ( 1.0,  1.0)
</code></pre><h5 id="2d-to-3d">2d to 3d</h5>
<p>To turn eight points into a cube we have to draw some lines. First, let&rsquo;s cleanup the code so it makes it easier for us to draw lines between pairs of points.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-typescript" data-lang="typescript"><span class="line"><span class="cl"><span class="c1">// src/canvas.ts
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kr">const</span> <span class="nx">POINTS</span>: <span class="kt">Point3D</span><span class="p">[]</span> <span class="o">=</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">  <span class="c1">// front face
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="p">{</span> <span class="nx">x</span><span class="o">:</span> <span class="o">-</span><span class="mf">0.5</span><span class="p">,</span> <span class="nx">y</span>: <span class="kt">0.5</span><span class="p">,</span> <span class="nx">z</span>: <span class="kt">1.0</span> <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="p">{</span> <span class="nx">x</span>: <span class="kt">0.5</span><span class="p">,</span> <span class="nx">y</span>: <span class="kt">0.5</span><span class="p">,</span> <span class="nx">z</span>: <span class="kt">1.0</span> <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="p">{</span> <span class="nx">x</span>: <span class="kt">0.5</span><span class="p">,</span> <span class="nx">y</span><span class="o">:</span> <span class="o">-</span><span class="mf">0.5</span><span class="p">,</span> <span class="nx">z</span>: <span class="kt">1.0</span> <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="p">{</span> <span class="nx">x</span><span class="o">:</span> <span class="o">-</span><span class="mf">0.5</span><span class="p">,</span> <span class="nx">y</span><span class="o">:</span> <span class="o">-</span><span class="mf">0.5</span><span class="p">,</span> <span class="nx">z</span>: <span class="kt">1.0</span> <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="c1">// back face
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="p">{</span> <span class="nx">x</span><span class="o">:</span> <span class="o">-</span><span class="mf">0.5</span><span class="p">,</span> <span class="nx">y</span>: <span class="kt">0.5</span><span class="p">,</span> <span class="nx">z</span>: <span class="kt">2.0</span> <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="p">{</span> <span class="nx">x</span>: <span class="kt">0.5</span><span class="p">,</span> <span class="nx">y</span>: <span class="kt">0.5</span><span class="p">,</span> <span class="nx">z</span>: <span class="kt">2.0</span> <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="p">{</span> <span class="nx">x</span>: <span class="kt">0.5</span><span class="p">,</span> <span class="nx">y</span><span class="o">:</span> <span class="o">-</span><span class="mf">0.5</span><span class="p">,</span> <span class="nx">z</span>: <span class="kt">2.0</span> <span class="p">},</span>
</span></span><span class="line"><span class="cl">  <span class="p">{</span> <span class="nx">x</span><span class="o">:</span> <span class="o">-</span><span class="mf">0.5</span><span class="p">,</span> <span class="nx">y</span><span class="o">:</span> <span class="o">-</span><span class="mf">0.5</span><span class="p">,</span> <span class="nx">z</span>: <span class="kt">2.0</span> <span class="p">},</span>
</span></span><span class="line"><span class="cl"><span class="p">];</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="c1">// 1. point pairs to draw lines between
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kr">const</span> <span class="nx">EDGES</span> <span class="o">=</span> <span class="p">[</span>
</span></span><span class="line"><span class="cl">  <span class="c1">// front face points
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="p">[</span><span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">  <span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">2</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">  <span class="p">[</span><span class="mi">2</span><span class="p">,</span> <span class="mi">3</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">  <span class="p">[</span><span class="mi">3</span><span class="p">,</span> <span class="mi">0</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">  <span class="c1">// back face points
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="p">[</span><span class="mi">4</span><span class="p">,</span> <span class="mi">5</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">  <span class="p">[</span><span class="mi">5</span><span class="p">,</span> <span class="mi">6</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">  <span class="p">[</span><span class="mi">6</span><span class="p">,</span> <span class="mi">7</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">  <span class="p">[</span><span class="mi">7</span><span class="p">,</span> <span class="mi">4</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">  <span class="c1">// connected corners
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="p">[</span><span class="mi">0</span><span class="p">,</span> <span class="mi">4</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">  <span class="p">[</span><span class="mi">1</span><span class="p">,</span> <span class="mi">5</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">  <span class="p">[</span><span class="mi">2</span><span class="p">,</span> <span class="mi">6</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">  <span class="p">[</span><span class="mi">3</span><span class="p">,</span> <span class="mi">7</span><span class="p">],</span>
</span></span><span class="line"><span class="cl"><span class="p">];</span>
</span></span></code></pre></div><p>Now let&rsquo;s draw some lines between these points, so it&rsquo;s easier to see the cube.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-typescript" data-lang="typescript"><span class="line"><span class="cl"><span class="kd">function</span> <span class="nx">setupCanvas2d</span><span class="p">(</span><span class="nx">element</span>: <span class="kt">HTMLCanvasElement</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="c1">// this wrapper will make sense later when we draw per frame
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="kr">const</span> <span class="nx">init</span> <span class="o">=</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="p">...</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nx">context2d</span><span class="p">.</span><span class="nx">fillRect</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="nx">CANVAS_SIZE</span><span class="p">,</span> <span class="nx">CANVAS_SIZE</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 1. set stroke style so the same color as the points
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="o">+</span>   <span class="nx">context2d</span><span class="p">.</span><span class="nx">strokeStyle</span> <span class="o">=</span> <span class="nx">COLOR</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="o">+</span>   <span class="nx">context2d</span><span class="p">.</span><span class="nx">lineWidth</span> <span class="o">=</span> <span class="mi">2</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// 2. start the path
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="o">+</span>   <span class="nx">context2d</span><span class="p">.</span><span class="nx">beginPath</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// 3. for each edge; draw line between the first and second point
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="k">for</span> <span class="p">(</span><span class="kr">const</span> <span class="nx">edge</span> <span class="k">of</span> <span class="nx">EDGES</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">      <span class="kr">const</span> <span class="kr">from</span> <span class="o">=</span> <span class="nx">screen</span><span class="p">(</span><span class="nx">project</span><span class="p">(</span><span class="nx">POINTS</span><span class="p">[</span><span class="nx">edge</span><span class="p">[</span><span class="mi">0</span><span class="p">]]),</span> <span class="nx">SCREEN_DIMENSIONS</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">      <span class="kr">const</span> <span class="nx">to</span> <span class="o">=</span> <span class="nx">screen</span><span class="p">(</span><span class="nx">project</span><span class="p">(</span><span class="nx">POINTS</span><span class="p">[</span><span class="nx">edge</span><span class="p">[</span><span class="mi">1</span><span class="p">]]),</span> <span class="nx">SCREEN_DIMENSIONS</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">      <span class="c1">// this moves the point without drawing a line
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>      <span class="nx">context2d</span><span class="p">.</span><span class="nx">moveTo</span><span class="p">(</span><span class="kr">from</span><span class="p">.</span><span class="nx">x</span><span class="p">,</span> <span class="kr">from</span><span class="p">.</span><span class="nx">y</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">      <span class="c1">// actually draw a line
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>      <span class="nx">context2d</span><span class="p">.</span><span class="nx">lineTo</span><span class="p">(</span><span class="nx">to</span><span class="p">.</span><span class="nx">x</span><span class="p">,</span> <span class="nx">to</span><span class="p">.</span><span class="nx">y</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="c1">// 4. submit the path to create a stroke
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>    <span class="nx">context2d</span><span class="p">.</span><span class="nx">stroke</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">  <span class="p">};</span>
</span></span><span class="line"><span class="cl">  <span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nx">init</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><pre tabindex="0"><code>(-1.0, -1.0)               ( 1.0, -1.0)
     ┌──────────────────────────┐
     │                          │
     │    3┌─────────────┐2     │
     │     │ \         / │      │
     │     │ 7┌───────┐6 │      │
     │     │  │       │  │      │
     │     │  │       │  │      │
     │     │  │       │  │      │
     │     │ 4└───────┘5 │      │
     │     │ /         \ │      │
     │    0└─────────────┘1     │
     │                          │
     └──────────────────────────┘
(-1.0,  1.0)               ( 1.0,  1.0)
</code></pre><p>Even though we can kinda see a cube, the next step will drive the 3-dimentionality of it home.</p>
<h4 id="rotating-the-cube">Rotating the cube</h4>
<p>Can you visualize a fully rotating cube in your mind? Mine isn&rsquo;t very consistent, it jumps around, and the color changes from frame to frame.</p>
<p>First, let&rsquo;s organize the code, so it&rsquo;s easier to animate the rotating cube.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-typescript" data-lang="typescript"><span class="line"><span class="cl"><span class="c1">// src/canvas.ts
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">function</span> <span class="nx">setupCanvas2d</span><span class="p">(</span><span class="nx">element</span>: <span class="kt">HTMLCanvasElement</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="kd">let</span> <span class="nx">context2d</span>: <span class="kt">CanvasRenderingContext2D</span> <span class="o">|</span> <span class="kc">null</span> <span class="o">=</span> <span class="kc">null</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1">// 1.1
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="kr">const</span> <span class="nx">init</span> <span class="o">=</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="p">...</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="o">-</span>   <span class="kr">const</span> <span class="nx">context2d</span> <span class="o">=</span> <span class="nx">element</span><span class="p">.</span><span class="nx">getContext</span><span class="p">(</span><span class="s1">&#39;2d&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="o">+</span>   <span class="nx">context2d</span> <span class="o">=</span> <span class="nx">element</span><span class="p">.</span><span class="nx">getContext</span><span class="p">(</span><span class="s1">&#39;2d&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1">// 1.2
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="o">+</span> <span class="kr">const</span> <span class="nx">clear</span> <span class="o">=</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"><span class="o">+</span>   <span class="nx">context2d</span><span class="p">.</span><span class="nx">fillStyle</span> <span class="o">=</span> <span class="nx">CLEAR_COLOR</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="o">+</span>   <span class="nx">context2d</span><span class="p">.</span><span class="nx">fillRect</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">0</span><span class="p">,</span> <span class="nx">CANVAS_SIZE</span><span class="p">,</span> <span class="nx">CANVAS_SIZE</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="o">+</span> <span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1">// 1.3
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="o">+</span> <span class="kr">const</span> <span class="nx">draw</span> <span class="o">=</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="nx">context2d</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="k">throw</span> <span class="k">new</span> <span class="nb">Error</span><span class="p">(</span><span class="s1">&#39;Failed to get drawing context&#39;</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="o">+</span>   <span class="nx">clear</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="o">+</span>   <span class="nx">context2d</span><span class="p">.</span><span class="nx">beginPath</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="nx">context2d</span><span class="p">.</span><span class="nx">stroke</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// 2. call draw 60 times a second
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="o">+</span>   <span class="nx">setTimeout</span><span class="p">(</span><span class="nx">draw</span><span class="p">,</span> <span class="mi">1000</span><span class="o">/</span><span class="mi">60</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">  <span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="nx">init</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">  <span class="c1">// 3. start the draw loop
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="o">+</span> <span class="nx">draw</span><span class="p">();</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>Now, let&rsquo;s actually rotate the cube. For now, we&rsquo;re just going to rotate the cube around the y-axis, which is another way to say that we&rsquo;re rotating in the xz-plane, so y coordinates of our cube remain unchanged.
If you&rsquo;d like to explore how the rotation formulae work, check out this excellent explanation: <a href="https://www.youtube.com/watch?v=EZufiIwwqFA">Rotation matrix derivation</a>. The video goes over rotation around the z-axis, however rotation around y-axis is quite similar.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-typescript" data-lang="typescript"><span class="line"><span class="cl"><span class="kr">const</span> <span class="nx">rotate_xz</span> <span class="o">=</span> <span class="p">({</span> <span class="nx">x</span><span class="p">,</span> <span class="nx">y</span><span class="p">,</span> <span class="nx">z</span> <span class="p">}</span><span class="o">:</span> <span class="nx">Point3D</span><span class="p">,</span> <span class="nx">angle</span>: <span class="kt">number</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="kr">const</span> <span class="nx">c</span> <span class="o">=</span> <span class="nb">Math</span><span class="p">.</span><span class="nx">cos</span><span class="p">(</span><span class="nx">angle</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="kr">const</span> <span class="nx">s</span> <span class="o">=</span> <span class="nb">Math</span><span class="p">.</span><span class="nx">sin</span><span class="p">(</span><span class="nx">angle</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">x</span>: <span class="kt">x</span> <span class="o">*</span> <span class="nx">c</span> <span class="o">-</span> <span class="nx">z</span> <span class="o">*</span> <span class="nx">s</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nx">y</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nx">z</span>: <span class="kt">x</span> <span class="o">*</span> <span class="nx">s</span> <span class="o">+</span> <span class="nx">z</span> <span class="o">*</span> <span class="nx">c</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="p">};</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span></code></pre></div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-typescript" data-lang="typescript"><span class="line"><span class="cl"><span class="c1">// src/canvas.ts
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kd">function</span> <span class="nx">setupCanvas2d</span><span class="p">(</span><span class="nx">element</span>: <span class="kt">HTMLCanvasElement</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="kd">let</span> <span class="nx">context2d</span>: <span class="kt">CanvasRenderingContext2D</span> <span class="o">|</span> <span class="kc">null</span> <span class="o">=</span> <span class="kc">null</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1">// 1.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="o">+</span> <span class="kd">let</span> <span class="nx">angle</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="o">+</span> <span class="kr">const</span> <span class="nx">dt</span> <span class="o">=</span> <span class="mi">1</span><span class="o">/</span><span class="nx">FPS</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="c1">// 2.
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="o">+</span> <span class="kr">const</span> <span class="nx">draw</span> <span class="o">=</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"><span class="o">+</span>   <span class="nx">angle</span> <span class="o">+=</span> <span class="p">(</span><span class="nb">Math</span><span class="p">.</span><span class="nx">PI</span> <span class="o">*</span> <span class="nx">dt</span><span class="p">)</span> <span class="o">/</span> <span class="mi">2</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="nx">context2d</span><span class="p">.</span><span class="nx">beginPath</span><span class="p">();</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="p">(</span><span class="kr">const</span> <span class="nx">edge</span> <span class="k">of</span> <span class="nx">EDGES</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="c1">// 3. rotate the points each frame as angle accumulates
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="o">-</span>     <span class="kr">const</span> <span class="kr">from</span> <span class="o">=</span> <span class="nx">screen</span><span class="p">(</span><span class="nx">project</span><span class="p">(</span><span class="nx">POINTS</span><span class="p">[</span><span class="nx">edge</span><span class="p">[</span><span class="mi">0</span><span class="p">]]),</span> <span class="nx">SCREEN_DIMENSIONS</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="o">-</span>     <span class="kr">const</span> <span class="nx">to</span> <span class="o">=</span> <span class="nx">screen</span><span class="p">(</span><span class="nx">project</span><span class="p">(</span><span class="nx">POINTS</span><span class="p">[</span><span class="nx">edge</span><span class="p">[</span><span class="mi">1</span><span class="p">]]),</span> <span class="nx">SCREEN_DIMENSIONS</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="o">+</span>     <span class="kr">const</span> <span class="kr">from</span> <span class="o">=</span> <span class="nx">screen</span><span class="p">(</span><span class="nx">project</span><span class="p">(</span><span class="nx">rotate_xz</span><span class="p">(</span><span class="nx">POINTS</span><span class="p">[</span><span class="nx">edge</span><span class="p">[</span><span class="mi">0</span><span class="p">]],</span> <span class="nx">angle</span><span class="p">)),</span> <span class="nx">SCREEN_DIMENSIONS</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="o">+</span>     <span class="kr">const</span> <span class="nx">to</span> <span class="o">=</span> <span class="nx">screen</span><span class="p">(</span><span class="nx">project</span><span class="p">(</span><span class="nx">rotate_xz</span><span class="p">(</span><span class="nx">POINTS</span><span class="p">[</span><span class="nx">edge</span><span class="p">[</span><span class="mi">1</span><span class="p">]],</span> <span class="nx">angle</span><span class="p">)),</span> <span class="nx">SCREEN_DIMENSIONS</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="p">...</span>
</span></span><span class="line"><span class="cl">  <span class="p">};</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">  <span class="p">...</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><h4 id="matrices">Matrices</h4>
<p>For this next trick, we&rsquo;re going to look at why people won&rsquo;t shut up about matrices when it comes to graphics programming.
Matrices are the hot shit, but what are they?
They are, among other things, a data structures that make it trivial to represent linear transformations like rotation.
Let&rsquo;s decompose our original rotation function into something that can be represented by a matrix.
The <a href="https://www.youtube.com/watch?v=EZufiIwwqFA">Rotation matrix derivation</a> video provides a great explanation of how to get to a matrix from the trignometry for 2 dimensions, however we need a solution for 3 dimensions.</p>
<pre tabindex="0"><code>┌    ┐   ┌         ┐┌   ┐   ┌                 ┐   ┌           ┐
│ x&#39; │   │ c  0 -s ││ x │   │ c*x + 0*y - s*z │   │ c*x - s*z │
│ y&#39; │ = │ 0  1  0 ││ y │ = │ 0*x + 1*y + 0*z │ = │       1*y │
│ z&#39; │   │ s  0  c ││ z │   │ s*x + 0*y + c*z │   │ s*x + c*z │
└    ┘   └         ┘└   ┘   └                 ┘   └           ┘
         │          │       │        
         │          input   output
         xz-rotation
</code></pre><h6 id="matrix-multiplication">Matrix multiplication</h6>
<p>If you&rsquo;re unfamiliar with matrix multiplication it can seem intimidating. However, it&rsquo;s a monster that seems less scary every time you come back and look at it again.</p>
<p>We take the first element of the first row of the rotation matrix, multiply it with the first element of the first column of the vector, and place it at the first position in the output matrix.</p>
<pre tabindex="0"><code>                  col      output           
                   ↓         ↓              
     ┌         ┐ ┌   ┐   ┌                 ┐
row →│ c  •  • │ │ x │   │ c*x +   • +   • │
     │ •  •  • │ │ • │ = │   • +   • +   • │
     │ •  •  • │ │ • │   │   • +   • +   • │
     └         ┘ └   ┘   └                 ┘
                   ↓               ↓        
     ┌         ┐ ┌   ┐   ┌                 ┐
    →│ •  0  • │ │ • │   │   • + 0*y +   • │
     │ •  •  • │ │ y │ = │   • +   • +   • │
     │ •  •  • │ │ • │   │   • +   • +   • │
     └         ┘ └   ┘   └                 ┘
                   ↓                     ↓  
     ┌         ┐ ┌   ┐   ┌                 ┐
    →│ •  • -s │ │ • │   │   • +   • - s*z │
     │ •  •  • │ │ • │ = │   • +   • +   • │
     │ •  •  • │ │ z │   │   • +   • +   • │
     └         ┘ └   ┘   └                 ┘
                                            
// completed row                            
     ┌         ┐ ┌   ┐   ┌                 ┐ 
     │ c  0 -s │ │ x │   │ c*x + 0*y - s*z │←
     │ •  •  • │ │ y │ = │   • +   • +   • │ 
     │ •  •  • │ │ z │   │   • +   • +   • │ 
     └         ┘ └   ┘   └                 ┘
</code></pre><p>Then the second row.</p>
<pre tabindex="0"><code>                  col                       
                   ↓                        
     ┌         ┐ ┌   ┐   ┌                 ┐ 
     │ •  •  • │ │ x │   │   • +   • +   • │ 
row →│ 0  1  0 │ │ y │ = │ 0*x + 1*y + 0*z │←
     │ •  •  • │ │ z │   │   • +   • +   • │ 
     └         ┘ └   ┘   └                 ┘
</code></pre><p>And finally the last one.</p>
<pre tabindex="0"><code>                  col                       
                   ↓                        
     ┌         ┐ ┌   ┐   ┌                 ┐ 
     │ •  •  • │ │ x │   │   • +   • +   • │ 
     │ •  •  • │ │ y │ = │   • +   • +   • │ 
row →│ s  0  c │ │ z │   │ s*x + 0*y + c*z │← 
     └         ┘ └   ┘   └                 ┘
</code></pre><p>Which brings us to the output.</p>
<pre tabindex="0"><code>┌    ┐   ┌         ┐┌   ┐   ┌                 ┐   ┌           ┐
│ x&#39; │   │ c  0 -s ││ x │   │ c*x + 0*y - s*z │   │ c*x - s*z │
│ y&#39; │ = │ 0  1  0 ││ y │ = │ 0*x + 1*y + 0*z │ = │       1*y │
│ z&#39; │   │ s  0  c ││ z │   │ s*x + 0*y + c*z │   │ s*x + c*z │
└    ┘   └         ┘└   ┘   └                 ┘   └           ┘
         │          │       │        
         │          input   output
         xz-rotation
i.e.
x&#39; = c*x - s*z
y&#39; = y
z&#39; = s*x + c*z
</code></pre><p>If you look closely at the final output you&rsquo;ll see that values of <code>x'</code>, <code>y'</code>, <code>z'</code> look the same as the return value of our <code>rotate_xz</code> function.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-typescript" data-lang="typescript"><span class="line"><span class="cl"><span class="c1">// src/canvas.ts
</span></span></span><span class="line"><span class="cl"><span class="c1"></span><span class="kr">const</span> <span class="nx">rotate_xz</span> <span class="o">=</span> <span class="p">({</span> <span class="nx">x</span><span class="p">,</span> <span class="nx">y</span><span class="p">,</span> <span class="nx">z</span> <span class="p">}</span><span class="o">:</span> <span class="nx">Point3D</span><span class="p">,</span> <span class="nx">angle</span>: <span class="kt">number</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="kr">const</span> <span class="nx">c</span> <span class="o">=</span> <span class="nb">Math</span><span class="p">.</span><span class="nx">cos</span><span class="p">(</span><span class="nx">angle</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="kr">const</span> <span class="nx">s</span> <span class="o">=</span> <span class="nb">Math</span><span class="p">.</span><span class="nx">sin</span><span class="p">(</span><span class="nx">angle</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">  <span class="k">return</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nx">x</span>: <span class="kt">x</span> <span class="o">*</span> <span class="nx">c</span> <span class="o">-</span> <span class="nx">z</span> <span class="o">*</span> <span class="nx">s</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nx">y</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nx">z</span>: <span class="kt">x</span> <span class="o">*</span> <span class="nx">s</span> <span class="o">+</span> <span class="nx">z</span> <span class="o">*</span> <span class="nx">c</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="p">};</span>
</span></span><span class="line"><span class="cl"><span class="p">};</span>
</span></span></code></pre></div><p>So how do we implement this in code? Let&rsquo;s first create a matrix class, that we can use to abstract away all this math.
I&rsquo;ll keep this section concise, the full diff is <a href="https://github.com/tauseefk/mnky/blob/56e06629906b80af94ace14e68e877c4c6b67ee8/src/mat4x4.ts">here</a>.</p>
<!--
This section feels a little contrived, why do we go from 3x3 matrix to 4x4?
What is row major?
-->
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-typescript" data-lang="typescript"><span class="line"><span class="cl"><span class="c1">// src/mat4x4.ts
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>
</span></span><span class="line"><span class="cl"><span class="kr">class</span> <span class="nx">Mat4x4</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nx">multiplyPoint</span><span class="p">(</span><span class="nx">point</span>: <span class="kt">Point3D</span><span class="p">)</span><span class="o">:</span> <span class="nx">Point3D</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="kr">const</span> <span class="p">{</span> <span class="nx">x</span><span class="p">,</span> <span class="nx">y</span><span class="p">,</span> <span class="nx">z</span> <span class="p">}</span> <span class="o">=</span> <span class="nx">point</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="kr">const</span> <span class="nx">m</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">mat</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nx">x</span>: <span class="kt">m</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span> <span class="o">*</span> <span class="nx">x</span> <span class="o">+</span> <span class="nx">m</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="o">*</span> <span class="nx">y</span> <span class="o">+</span> <span class="nx">m</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span> <span class="o">*</span> <span class="nx">z</span> <span class="o">+</span> <span class="nx">m</span><span class="p">[</span><span class="mi">3</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">      <span class="nx">y</span>: <span class="kt">m</span><span class="p">[</span><span class="mi">4</span><span class="p">]</span> <span class="o">*</span> <span class="nx">x</span> <span class="o">+</span> <span class="nx">m</span><span class="p">[</span><span class="mi">5</span><span class="p">]</span> <span class="o">*</span> <span class="nx">y</span> <span class="o">+</span> <span class="nx">m</span><span class="p">[</span><span class="mi">6</span><span class="p">]</span> <span class="o">*</span> <span class="nx">z</span> <span class="o">+</span> <span class="nx">m</span><span class="p">[</span><span class="mi">7</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">      <span class="nx">z</span>: <span class="kt">m</span><span class="p">[</span><span class="mi">8</span><span class="p">]</span> <span class="o">*</span> <span class="nx">x</span> <span class="o">+</span> <span class="nx">m</span><span class="p">[</span><span class="mi">9</span><span class="p">]</span> <span class="o">*</span> <span class="nx">y</span> <span class="o">+</span> <span class="nx">m</span><span class="p">[</span><span class="mi">10</span><span class="p">]</span> <span class="o">*</span> <span class="nx">z</span> <span class="o">+</span> <span class="nx">m</span><span class="p">[</span><span class="mi">11</span><span class="p">],</span>
</span></span><span class="line"><span class="cl">    <span class="p">};</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>Now we can update the caller to use the matrix instead.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-typescript" data-lang="typescript"><span class="line"><span class="cl"><span class="c1">// src/canvas.ts
</span></span></span><span class="line"><span class="cl"><span class="c1"></span>  <span class="kr">const</span> <span class="nx">draw</span> <span class="o">=</span> <span class="p">(</span><span class="nx">shape</span>: <span class="kt">Shape</span><span class="p">)</span> <span class="o">=&gt;</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="p">...</span>
</span></span><span class="line"><span class="cl">    <span class="nx">angle</span> <span class="o">+=</span> <span class="p">(</span><span class="nb">Math</span><span class="p">.</span><span class="nx">PI</span> <span class="o">*</span> <span class="nx">dt</span><span class="p">)</span> <span class="o">/</span> <span class="mi">2</span><span class="p">;</span>
</span></span><span class="line"><span class="cl"><span class="o">+</span>   <span class="kr">const</span> <span class="nx">rotationMatrix</span> <span class="o">=</span> <span class="nx">Mat4x4</span><span class="p">.</span><span class="nx">fromRotationXZ</span><span class="p">(</span><span class="nx">angle</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="p">(</span><span class="kr">const</span> <span class="nx">face</span> <span class="k">of</span> <span class="nx">shape</span><span class="p">.</span><span class="nx">faces</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="p">...</span>
</span></span><span class="line"><span class="cl"><span class="o">-</span>     <span class="kr">const</span> <span class="kr">from</span> <span class="o">=</span> <span class="nx">screen</span><span class="p">(</span><span class="nx">project</span><span class="p">(</span><span class="nx">rotate_xz</span><span class="p">(</span><span class="nx">POINTS</span><span class="p">[</span><span class="nx">edge</span><span class="p">[</span><span class="mi">0</span><span class="p">]],</span> <span class="nx">angle</span><span class="p">)),</span> <span class="nx">SCREEN_DIMENSIONS</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="o">-</span>     <span class="kr">const</span> <span class="nx">to</span> <span class="o">=</span> <span class="nx">screen</span><span class="p">(</span><span class="nx">project</span><span class="p">(</span><span class="nx">rotate_xz</span><span class="p">(</span><span class="nx">POINTS</span><span class="p">[</span><span class="nx">edge</span><span class="p">[</span><span class="mi">1</span><span class="p">]],</span> <span class="nx">angle</span><span class="p">)),</span> <span class="nx">SCREEN_DIMENSIONS</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="o">+</span>     <span class="kr">const</span> <span class="kr">from</span> <span class="o">=</span> <span class="nx">screen</span><span class="p">(</span><span class="nx">project</span><span class="p">(</span><span class="nx">rotationMatrix</span><span class="p">.</span><span class="nx">multiplyPoint</span><span class="p">(</span><span class="nx">POINTS</span><span class="p">[</span><span class="nx">edge</span><span class="p">[</span><span class="mi">0</span><span class="p">]])),</span> <span class="nx">SCREEN_DIMENSIONS</span><span class="p">);</span>
</span></span><span class="line"><span class="cl"><span class="o">+</span>     <span class="kr">const</span> <span class="nx">to</span> <span class="o">=</span> <span class="nx">screen</span><span class="p">(</span><span class="nx">project</span><span class="p">(</span><span class="nx">rotationMatrix</span><span class="p">.</span><span class="nx">multiplyPoint</span><span class="p">(</span><span class="nx">POINTS</span><span class="p">[</span><span class="nx">edge</span><span class="p">[</span><span class="mi">1</span><span class="p">]])),</span> <span class="nx">SCREEN_DIMENSIONS</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span></code></pre></div><p>This concludes the first part in the series, of Whither GPU.
In the next part, we&rsquo;ll write a wireframe renderer using WebGPU.</p>
]]></content:encoded></item></channel></rss>