Skip to the story
Seen from the sky, a humpback whale glides past a small wooden rowing boat on a deep blue sea streaked by the swell.

The making of

Dérive

How one person painted an endless ocean in a web page, and taught it to breathe.

Thibaud Perrin — design, code, music

I wanted a gentle game about the ocean. Not a game to win: a place to drift, where a whale passes under the boat because it was going that way, and you stop rowing to watch it.

Dérive is a rowing boat seen from the sky, on a sea that never ends. You row, you watch, and you fill the notebook of Élise Marceau, a naturalist who set out in 1911 and whose pages float back in bottles. There is no score and no clock. The sea takes the time it takes.

I made it alone: the design, every line of code, the art direction and the music. It runs in a browser, on a phone as well as on a desk, with nothing to install and no server of mine behind it. This page is how it was made, and why it looks the way it does.

A sea painted pixel by pixel

There is no picture of the sea anywhere in Dérive. Every frame, a WebGL2 shader works out each pixel of the water from scratch: two bands of swell, two octaves of wind ripples, all summed into a height, and the exact slope of that height under every single pixel. The light comes from that slope. Where a little facet of water tilts just right to mirror the sun into your eye, it flashes.

That is how the glitter works. At noon, a fine white sparkle all over the view. When the sun sits low, the flashes stretch into gold strokes along its bearing and gather into a long road of light coming in from its side, the way the real one follows you along a beach. At night, a thin silver path under the moon.

The rowing boat alone on dark teal water at golden hour, the sun’s reflections scattered across the swell as hundreds of small gold flecks.
The Grand bleu at golden hour: each fleck of gold is a facet of water mirroring the sun

Then the whole frame goes through a painted pass. Colours are smeared along a brush field that drifts with the sea, embossed a little, grained like paper, so the ocean reads as watercolour rather than as a render. The smear is sized in screen pixels, not in render pixels: when a tired phone has to draw the scene at half resolution, the painting keeps the same brush.

Making that run at sixty frames a second on a phone was most of the work. Anything that changes over metres rather than pixels (where the reefs are, which sea you are in, the large shading of the swell) is drawn once a frame into small textures at an eighth of the size and read back filtered: three texture reads instead of twelve to twenty noise evaluations a pixel, five milliseconds saved a frame over the shallows. The sea, the seabed, the creatures and what floats all go into six draw calls before the painted pass, plus one per 3D model on screen.

The sea had to feel painted, never rendered. And it had to stay painted on the smallest phone.

A day lasts ten minutes: a minute of dawn, five of day, a minute and a half of dusk, two and a half of night. The colours do not simply darken. Each hour has its own palette, eased along curves: cold blue then pink at dawn, gold then violet at dusk. The weather is a function of the sea and the clock, so everyone sailing the same sea, together, sees the same rain at the same moment, with nothing sent between them.

  • The lagoon at dawn, washed in pale pink.dawn
  • The same lagoon in the gold of late afternoon.golden hour
  • The same lagoon at dusk, gone violet.dusk
  • The same lagoon at night, in deep blue.night
One lagoon, the boat held still, four hours of the same ten-minute day

A seabed that grows by itself

Under the shallows there are meadows of seagrass, coral heads in the lagoon, forests of kelp leaning with the surge. None of it is modelled, and none of it is stored. It grows on the graphics card, every frame, out of nothing but numbers.

The trick is that the seabed has no vertex buffers at all. The floor is cut into cells six metres wide, anchored to the world. The vertex shader receives only its own index, gl_VertexID, and from it works out which cell it belongs to and which slot in that cell. A small hash of the cell and the slot gives the blade a place, a length, a lean. Then it looks at how dense the meadow is there: if the meadow is thin, the blade folds into a triangle of zero size and the GPU skips it.

Eight cells of seabed, six metres each: in every cell the same slots are hashed to a place; where the meadow is dense they become blades, where it thins they collapse to nothing.
Eight cells of six metres: the same hashed slots in each, kept as blades where the meadow is dense, folded to nothing where it thins

A cell of meadow holds up to 640 blades, five vertices each. They are not instanced: tiny instances cost the GPU per instance, not per vertex, so the blades are drawn back to back in one long triangle strip, joined by degenerate triangles. Measured, the same meadow costs about forty per cent less that way. Slide across the picture below to lift the meadow off the sand.

The same spot without its seabed: green-tinted water over bare sand ripples. A seagrass meadow seen from above, thousands of blades combed by the current, the rowing boat in the middle.
with the seabedwithout

The blades bend with the current in bands that roll across the meadow. And they notice you. Every creature and the boat itself are splatted, each frame, into a small disturbance texture that wraps every eighty metres and fades a little at a time: near the floor, near the surface, in between. The blades lie down and bend away from whatever passes, anemones pull in, the kelp canopy parts around the hull and closes again behind it.

The rowing boat crossing a seagrass meadow, its wake trailing behind as the blades stir beneath it.
Rowing across the Herbiers
The rowing boat threading through a kelp forest between granite boulders, golden fronds streaming at the surface.
Threading the kelp forest, past a sea stack

Each sea has its own floor. In the lagoon, brain and table corals, staghorn with pale tips, sea fans and soft corals that sway, all in faded pinks, ochres and creams, gathered into round coral heads over the sand. In the kelp forest, olive-gold stipes rise from their holdfasts toward fronds streaming just under the surface, over granite crusted with pink coralline and sea stars, and the water between them darkens and blurs the deeper they go.

A turquoise lagoon over a reef of rounded corals in soft pastel colours, the boat drifting above it.
The lagoon’s coral heads
A kelp forest from above: golden fronds over dark rocky reefs and pale shingle.
The kelp forest, leaning with the surge

Creatures that swim, not slide

Fish, sharks, turtles, rays and whales live in a small simulation written in Rust and compiled to WebAssembly. It is deliberately tiny: no standard library, no allocator, no imports, all its state in one fixed block of memory, so the page reads the creatures’ positions straight from it without copying a byte. Compiled, it weighs 11.7 kilobytes. Schools follow their leader, sharks circle, whales rise toward the surface in the seconds before they breathe and spout only once their back is near it. A copy of the same simulation in JavaScript takes over where WebAssembly is blocked, and a test runs the two in lockstep so they never drift apart.

Then each animal has to move like itself. The modeller paints weights into each model’s vertex colours: red for the body’s wave, green for the fins or wings, blue for thin parts that flutter. The vertex shader sends a wave from the head down the body, beats the fins, ripples the edges, and reads the creature’s turn from frame to frame, so the spine bends along the arc it swims and the body banks into the turn. A whale sweeps round in a long curve the length of itself; a manta tilts its wings like a glider.

Live, in your browser: the game’s own model and swim motion. Drag it to turn it. Megaptera novaeangliae

Depth is painted, not faded. A body under water is never see-through: the back catches more light than the flanks, the water’s own colour takes over point by point, the edges go first, and by day the ripples of sunlight run over backs near the surface. Seen from the sky, a whale twelve metres down is a shape you guess before you see it.

Some animals you only meet if you are patient. Stay calm beside them, oars still, with the right bait in the water, and the encounter fills like a held breath. When it does, the whale answers with a breath beside the boat. If friends are sailing with you, it counts for them too.

The notebook: pencil first, then watercolour

Every animal you cross goes into Élise’s notebook, fifty-one species that all exist, but one. How the notebook draws them follows how well you know them. Crossed once, a pencil sketch: a wavering double outline, hatching by the light, graphite caught on the paper’s tooth. Seen often enough, its colours come. And once you grind the pigments it needs from what the sea gives you (nacre for white, sea glass for blue, rust for ochre, soot for black), it becomes a watercolour plate: washes bleeding past the outline, pigment pooled at the wet edges, the lit back left as bare paper.

None of these plates were painted by hand. The game draws them from the same 3D models, in two passes over the model’s colours and shape, the first time you open the page.

A pencil sketch of a humpback whale, hatched lines over a wavering outline. crossed: the pencil
The same humpback whale as a watercolour plate in greys and blues. painted: the watercolour

Loading, as a watercolour

A game has to load. I wanted the waiting to be part of it. The page opens on paper; the ink writes “Dérive” letter by letter, then a watercolour wash spreads over one of four sea charts I painted, and the wash is the progress bar. When the sea behind is ready, you choose to enter with sound or in silence, and the painted chart plunges into the live ocean.

The wash is a twenty-two kilobyte script with its own little WebGL program: seven stains creeping out through a torn displacement, a darker band of wet pigment at their fronts, the chart swirling and settling as it fills. It runs in a worker on an offscreen canvas, so the game’s heavy start never makes it stutter. The title’s two fonts are inlined as subsets of a kilobyte or two, so the ink writes on the very first paint.

The loading screen early on: “Dérive” in ink on cream paper, a watercolour sea chart only half washed in, its edges still ragged. The same loading screen a few seconds later, the whole sea chart washed in around the title.
Scroll on: the chart washes in, as it does while the sea loads

A world without a server

Dérive is a static site. There is no game server, no database of mine, nothing to keep running. Every sea comes from one number, readable as a short code like J9C-HA4W: the same code gives the same reefs, the same people, the same weather, on any device. Your notebook lives on your device. If you make an account, your game is encrypted and signed on your device before a copy is left on public relays, so nobody else can read it, not even me.

Sailing together works the same way. The boats find each other through those relays and then talk directly, browser to browser, over WebRTC; the room code is also its password. A boat’s state is fifty-two bytes, ten times a second. Whoever leads a group simulates the creatures for everyone near, so you all watch the same whale.

And the sea really is endless. Floating-point numbers lose their precision far from the origin, and at fifty kilometres the slowest fish would have frozen. So the world quietly re-centres itself on the boat as you row, and stays exact out to sixteen thousand kilometres: four months of rowing in a straight line.

In a few numbers

Frames a second, on a phone60
The creature simulation, compiled11.7 KB
Seagrass blades in one six-metre cell640
Species in Élise’s notebook51
Possible seas, each from a single number4.3 billion
Kilometres before the sea loses precision16,000
Servers of mine0

The music

I composed the music too. Each of the eight seas has its own piece, the lagoon’s light and warm, the cold seas’ slow and wide, the abyss low and patient; night has its own, and a storm takes over everything while it lasts. As you row from one sea into the next, the pieces cross-fade over six seconds, and they duck a little when a shark comes near.

The rest of the sound is made live by the browser: the sea’s hush, the creak of the oars, the whales’ breath, rain, thunder rolling in a few seconds after the flash, further or closer. Headphones help.

“Accueil”, the theme of the welcome screen — Thibaud Perrin

The sea is waiting.

Dérive is free, in your browser, on a phone or a desk. Headphones on, oars out.

Take to the sea

Thibaud Perrin, designer, developer and composer. Something to say? Write to me.