Back to portfolio

How I built this.

Everything that moves on this site is drawn live by code running on your graphics card. No videos, no 3D models. Here's how the main pieces work, and what keeps them fast.

The fluid behind my name.

A real-time fluid simulation, with the name drawn as glass you can see the fluid through.

The colour at the top of the homepage is a simplified version of Jos Stam's stable fluids method, the same idea film studios use for smoke. The fluid's velocity lives in a 128-cell grid and its colour in a much finer one, both stored in GPU textures. Every frame runs the same short pipeline, each step a small shader that reads one texture and writes another.

One frame of the fluid simulation Six steps in a row: push, swirl, thicken, balance, carry and draw, with an arrow looping back to the start for the next frame. Pushcursor addsforce and dye Swirlput back smalleddies Thickenspread motionlike syrup Balanceno fluid madeor lost (×20) Carrymove colouralong the flow Drawshade it, addthe glass next frame, about 60 times a second
"Balance" is the expensive part: twenty passes that nudge the velocity until no cell has more fluid flowing in than out, which is what makes it swirl like a liquid instead of spreading like paint.

The glass letters

To make the name look like glass, its letters are drawn twice into hidden images at their exact positions on the page: once sharp, as a mask, and once blurred, as a height map with rounded edges. Inside the letters, the shader reads the fluid from slightly off to the side, in the direction the height map slopes, which is what a curved lens does. Red, green and blue are offset by slightly different amounts, so the edges split into a faint rainbow. A slow bed of colour drifts behind the letters only, so the glass always has something to bend even when nobody has touched the fluid.

See it live

The glass S.

A 3D glass letter with no model behind it: the shape is a formula, and every pixel traces its own ray of light through it.

The S is two arcs, each 250 degrees of a circle, one a half-turn copy of the other. A signed distance function gives, for any point in space, how far it is from the nearest part of that shape. Pushing the 2D outline into 3D with bevelled edges is a few more lines of the same function.

To draw it, each pixel sends a ray forward, and the distance function says how far the ray can safely jump without hitting anything. It jumps that far and asks again, closing in on the surface in a handful of steps. This is raymarching.

A ray of light passing through the glass S A white ray enters a glass slab and bends. Inside, it splits into red, green and blue, which leave at slightly different angles and land on the coloured lamp behind at slightly different places. one stroke of the S, cut through ray from your eye the lamp behind red, green and blue bend by slightly different amounts
Light bends where it enters the glass and again where it leaves. Using three slightly different refractive indices, one per colour, is what produces the rainbow fringes on its edges.

Bending, splitting and casting light

Where the ray hits, it bends into the glass (Snell's law, with a refractive index of 1.52, like window glass), travels through by raymarching from the inside, and bends again on the way out. That happens three times per pixel, with the index nudged up and down by 5% for red and blue, which is dispersion. If a ray meets the inside surface too steeply to escape, it reflects instead: total internal reflection, which is why the edges flash silver as it turns. The exit ray then looks up a slowly drifting lamp of blue, purple and pink behind the S, whose light also spills faintly across the whole section.

The shadow is traced the same way, from the page back toward the light. Where the glass bunches light together the page gets brighter, and where it spreads light apart it dims; the shader measures that by comparing where neighbouring pixels' light ends up. Those bright lines are caustics, like the patterns at the bottom of a pool. Every pixel on the S's edge takes four samples, so the silhouette stays smooth.

See it live

The falling tools.

Real rigid-body physics, applied to real text.

Each tool name is a rounded-rectangle body in Matter.js, the one library on the site. The physics runs at a fixed 60 steps a second; after each step, every chip's position and angle are copied onto the actual list item as a CSS transform. The text stays real text, so it can still be read aloud, selected and searched. Bodies that come to rest are put to sleep, and when they all are, the loop stops until you touch one.

With a mouse you can grab a chip: a spring joins it to the cursor and you can throw it. On a touch screen a tap flicks it instead, so the page still scrolls normally. If the library can't load, or you've asked your device to reduce motion, the tools stay as a simple wrapped list.

See it live

The galaxy.

42,000 particles, simulated entirely on the graphics card: a flock of starlings that settles into a spiral galaxy.

Moving tens of thousands of points in JavaScript each frame would be slow, so the simulation runs in a vertex shader using transform feedback. Each particle's position and velocity sit in one GPU buffer; the shader reads them, works out the next step and writes the results into a second buffer. Next frame the two swap. The CPU never touches a single particle.

Two buffers swapping every frame Buffer A feeds the update shader, which writes buffer B, which is drawn. Next frame the roles swap. Buffer Athis frame's stars Update shaderflock, settle, scatter Buffer Bnext frame's stars swap, and draw B as points
"Ping-ponging" between two buffers means a particle's next position is always computed from a complete, unchanged copy of the last one.

While flying, each particle follows a curl-noise wind: a flow made from the swirl of a smooth noise pattern, which on its own never piles particles up or tears them apart, so the flock stretches and folds like starlings instead of clumping. A gentle pull toward a wandering centre keeps it together. After a few seconds, a spring pulls every particle toward its own home on a two-armed logarithmic spiral, tilted to look like a galaxy seen at an angle, with inner stars orbiting faster than outer ones. Near the cursor a particle gets "excited" and flies free again, then calms down and drifts home.

See it live

The black hole.

A ray tracer in which light falls.

Normally a ray of light travels in a straight line. Here, every pixel's ray is traced backwards from the camera in small steps, and at each step it is pulled toward the hole. For a non-spinning (Schwarzschild) black hole, light bends as if it felt an extra force of a = −1.5·h²·r / |r|⁵, where r is the ray's position and h its angular momentum around the hole, which stays the same for the whole path. That force grows so steeply near the centre that rays passing close enough never come back out.

How the black hole bends light Three rays leave the camera on the left. One passes far away, nearly straight. One curves over the hole and lands on the far side of the disc, which is why it appears above the hole. One falls in. the hole far side of the disc camera bent over the top falls in: black
Light from the far side of the disc is bent over the top of the hole on its way to you, so you see the disc's back half arching above and below the shadow, as in the film Interstellar.

The gas disc is a thick, glowing volume rather than a flat sheet. Rays crossing it step through in small increments, picking up light and being dimmed by it as they go. Its marbled pattern is noise fed through more noise (domain warping), recomputed into a texture every frame so it keeps evolving, and each ring of gas is carried round at its own orbital speed, faster near the centre. That difference in speed is what shears the clumps into fine streaks. Gas moving toward you really would look brighter (Doppler beaming); Interstellar toned that down to keep the disc even, and so does this.

See it in the lab

Also in the lab.

Each experiment has its own "How it works" note.

Keeping it fast and kind.

An effect that drains your battery or makes you feel sick isn't a good effect.

Questions about any of it?

I'm happy to talk through how any of these work, or build something with you.

Open the lab Get in touch