Skip to main content

Web visualization

If the last decade has taught us anything, it’s that ideas don’t win because they’re right—they win because someone makes them impossible to ignore. Grant Sanderson’s 3Blue1Brown turns linear algebra into visual poetry; Bartosz Ciechanowski dissects gyroscopes, cameras, and orbits with animations so clear you can almost feel the math under your fingertips. Their secret isn’t gimmicky graphics—it’s a ruthless commitment to visualizing complexity until mystery gives way to intuition.

We’re drowning in technical detail and Powerpoint and AI is going to create so much more complexity and increase the velocity of work. The future of education and professional communication will belong to the people who give people the space to interact with inner gears turning. Time to learn web graphics.

Like high school math? You have an edge here. Computer graphics are all basic math with rapid feedback. Just as spreadsheets once democratized finance, interactive visualization is becoming a core literacy for anyone who trades in ideas:

  • Engineers need to convince non-technical stakeholders that a control-theory tweak saves fuel—seeing the PID loop animate beats ten paragraphs.
  • Policy analysts must untangle cyber-deterrence models for decision-makers who have minutes, not hours.
  • Students can leapfrog rote memorization by exploring vector calculus as a living landscape instead of a static chalkboard.

The <canvas> element in HTML is a powerful drawing surface that enables dynamic, scriptable rendering of 2D shapes and bitmap images directly in the browser. Initially introduced for simple visual tasks like graphs and animations, its capabilities have vastly expanded with the addition of WebGL, allowing developers to access the GPU and render true 3D graphics. Recent advancements in JavaScript performance, browser rendering engines, and hardware acceleration have made it possible to create rich, interactive visualizations—from games and simulations to data dashboards and physics demos—without plugins. The rise of frameworks like Three.js and libraries like D3 or PixiJS has further lowered the barrier, helping developers tap into the full potential of canvas-based rendering. In a world overwhelmed by static documents and complex systems, the canvas offers a blank slate where inner workings—mathematical, physical, or conceptual—can be made visible and intuitive.

We’ll start small—rotating a humble cube with the Canvas API—then escalate to articulated mechanisms, dynamic differential-equation plots, and narrative data stories. There is a trade between powerful visualizations where you have to learn the math or libraries that do the math for you and get you visualizing faster. Will look at both, starting with hand-jamming visualizations.

First, lets make a basic block. You can click on all my links to see the code. Click zoom to 0.5 below if you want to see the whole visualization.

See the Pen basic cube by Tim Booher (@tbbooher) on CodePen.

What is happening here? I built a 3D cube defined by its vertices and edges, and the goal is to render a 2D projection of this cube on a HTML5 canvas, rotating it interactively around the Y-axis based on a slider input. The cube’s corners are represented as 3D points in space. These are the 8 corners of a cube centered at the origin, with side length 2 units.

$$
\text{vertices} = \{ (-1,-1,-1), (1,-1,-1), (1,1,-1), (-1,1,-1), (-1,-1,1), (1,-1,1), (1,1,1), (-1,1,1) \}
$$

Edges connect pairs of vertices by their indices, each tuple connects two vertices, defining the cube’s edges.

$$
\text{edges} = \{(0,1), (1,2), (2,3), (3,0), (4,5), (5,6), (6,7), (7,4), (0,4), (1,5), (2,6), (3,7)\}
$$

To rotate the cube around the Y-axis by an angle \(\theta\), each vertex \((x, y, z)\) is transformed using the rotation matrix for the Y-axis:

$$
R_y(\theta) = \begin{bmatrix} \cos \theta & 0 & -\sin \theta \\ 0 & 1 & 0 \\ \sin \theta & 0 & \cos \theta \end{bmatrix}
$$

Multiplying this matrix by the vertex vector gives the rotated coordinates:

$$
\begin{aligned} x’ &= x \cos \theta – z \sin \theta \\ y’ &= y \\ z’ &= x \sin \theta + z \cos \theta \end{aligned}
$$

To display the 3D cube on a 2D canvas, the 3D points are projected onto the 2D plane. I used a simple perspective projection, assuming a camera looking along the z-axis. The scale factor depends on the depth \(z’\) to simulate perspective. Here \(d = 300\) is an arbitrary distance scaling factor. \(c = 4\) is an offset to keep the cube in front of the camera (to avoid division by zero).

$$\text{scale} = \frac{d}{z’ + c}$$

From this, the projected 2D coordinates (X, Y) are:

$$\begin{aligned} X &= x’ \times \text{scale} + \frac{\text{canvas width}}{2} \\ Y &= y \times \text{scale} + \frac{\text{canvas height}}{2} \end{aligned}$$

Now, let’s add some shading, and simplify the code. If we define the cube by the vertices \(\mathbf{v}_i = (x_i, y_i, z_i)\) and faces as collections of these vertices. To simulate 3D rotation, each vertex is first rotated around the Y-axis by angle \(\theta_y\) and the X-axis by angle \(\theta_x\) using rotation matrices:

$$R_y(\theta_y) = \begin{bmatrix} \cos\theta_y & 0 & \sin\theta_y \\ 0 & 1 & 0 \\ -\sin\theta_y & 0 & \cos\theta_y \end{bmatrix}, \quad R_x(\theta_x) = \begin{bmatrix} 1 & 0 & 0 \\ 0 & \cos\theta_x & -\sin\theta_x \\ 0 & \sin\theta_x & \cos\theta_x \end{bmatrix}$$

The combined rotation is applied as \(\mathbf{v}_i’ = R_x(\theta_x) R_y(\theta_y) \mathbf{v}_i\).
Next, each rotated vertex is projected to 2D canvas coordinates using a perspective projection:

$$X = \frac{f x’}{z’ + d} + \frac{W}{2}, \quad Y = \frac{f y’}{z’ + d} + \frac{H}{2}$$

where \(f\) is a focal length scale, \(d\) offsets the camera depth, and \(W\),\(H\) are canvas dimensions. This allows us to shade the cube faces with face normals \(\mathbf{n}\) are computed by taking the cross product of two edges of each face:

$$\mathbf{n} = \frac{(\mathbf{v}_1’ – \mathbf{v}_0’) \times (\mathbf{v}_2’ – \mathbf{v}_0’)}{\|(\mathbf{v}_1’ – \mathbf{v}_0’) \times (\mathbf{v}_2’ – \mathbf{v}_0’)\|}$$


Brightness for each face is determined by the dot product between its normalized normal \(\mathbf{n}\) and a fixed light direction \(\mathbf{l}\), \(b = \max(0, \mathbf{n} \cdot \mathbf{l})\). Faces are then rendered back-to-front based on average depth (Painter’s algorithm) with fill color intensity proportional to b, producing realistic shading. User interaction adjusts the rotation angles \(\theta_x\), \(\theta_y\) either via a slider or mouse drag, causing the cube to re-render dynamically with updated shading.

See the Pen basic cube 2 by Tim Booher (@tbbooher) on CodePen.

Now, let’s build a 2D physics simulation with two boxes moving inside a canvas, bouncing off each other and the walls realistically. For this, two axis-aligned rectangles (boxes) move with velocities \(\mathbf{v} = (v_x, v_y)\). Each frame, their positions \(\mathbf{p} = (x, y)\) update as \(\mathbf{p} \leftarrow \mathbf{p} + \mathbf{v}\).

When a box hits a canvas boundary, its velocity component perpendicular to that wall reverses sign to simulate an elastic bounce:

$$v_x \leftarrow -v_x \quad \text{or} \quad v_y \leftarrow -v_y$$

Collision detection uses axis-aligned bounding box (AABB) overlap tests: two boxes B_1, B_2 collide if their intervals overlap on both axes:

$$B_1.x + B_1.w > B_2.x \quad \text{and} \quad B_1.x < B_2.x + B_2.w\\
B_1.y + B_1.h > B_2.y \quad \text{and} \quad B_1.y < B_2.y + B_2.h$$

On collision, the boxes’ velocities along the collision axis swap using the 1D elastic collision formula with masses proportional to box area. The collision axis is chosen based on the smaller overlap dimension (X or Y). The boxes are separated by half the overlap distance along that axis to prevent sticking.

$$v_1’ = \frac{v_1 (m_1 – m_2) + 2 m_2 v_2}{m_1 + m_2}, \quad v_2’ = \frac{v_2 (m_2 – m_1) + 2 m_1 v_1}{m_1 + m_2}$$

See the Pen basic cube with shading by Tim Booher (@tbbooher) on CodePen.

Hopefully this feels empowering. You now have a tidy little playground that shows off three core tricks of browser-side graphics: a \(\text{Canvas}\) cube that rotates via real \(R_x\,R_y \)matrices and a perspective divide, face shading driven by the dot-product \( \mathbf{n} \cdot \mathbf{l} \) , and a mini 2-D physics engine where rectangles exchange momentum and rebound off the walls. With these foundations in place you can start remixing at will—tweak the light vector to watch the cube’s highlights skate across its faces, change masses or elasticities to see how collisions feel, introduce additional shapes, or even swap the perspective projection for an orthographic one to compare visual effects.

Build boldly!

Author: Tim Booher

Be the first to write a comment.

Leave a Reply

Required fields are marked *