Isle of Savreen: a tank of water you can tip
The water simulator goes 3D: a glass cube of painted water with a rusting shipwreck and mossy boulders inside, seen like a tilt-shift photograph of a model. Tip, spin and slide the tank and the water answers. How FLIP turns a few thousand particles into water, how moving the tank becomes forces the water feels, and how I quieted my laptop's fans.

Isle of Savreen is the 3D follow-up to my 2D water simulator: a glass cube of water, a meter on a side, with a rusting fishing boat run aground among mossy boulders inside. You look down on it like a model on a table, and the arrows let you pick the tank up. Tip it and the water pours to the low side and breaks over the rocks; jerk it sideways and it slams into the far wall and climbs it. As in the 2D version, every bit of water is a brush stroke, deep blue when slow and white foam when fast, and the sound is built from the same physics.
How FLIP turns particles into water
The water is about 7,500 particles, each a 2.5 cm drop that carries its own position and velocity. Particles are good at being water: wherever there are particles there’s water, everywhere else is air, so splashes, droplets and a free surface come for free. What particles are bad at is the one rule water lives by, that it can’t be squeezed. Checking that every bit of water has as much flowing out as flowing in is easy on a grid and hard among scattered points.
FLIP (Fluid-Implicit-Particle) uses both. The particles carry the water; a coarse grid, 20 × 20 × 20 cells of 5 cm, enforces incompressibility. Brackbill and Ruppel invented it for plasma physics in 1986, and Zhu and Bridson brought it to graphics in 2005; it’s what film effects use for splashing water. Every 1/60 of a second, each step goes:
- Move the particles. Add gravity (and the tank’s forces, below), then move each particle along its velocity. For a moment the water just flies.
- Keep them spaced out. Anywhere two particles overlap, push them apart. Without this they clump in some places and leave gaps in others.
- Collide. Push any particle that ended up in a wall, the ship or a rock back out to the surface, and stop it moving into it.
- Particles to grid. Each particle spreads its velocity onto the eight nearest cell faces, weighted by how close it is. The grid stores velocities on cell faces rather than centers, so the flow through each face is exactly what’s needed to count what enters and leaves each cell.
- Make it incompressible. For every cell with water in it, add up the flow out through its six faces. In real water that’s zero. Here it won’t be, so nudge the faces until it is: push flow out of cells that are being crammed and into ones that are draining. Each fix disturbs the neighbors, so the grid is swept 40 times. That nudging is the pressure in the Navier–Stokes equations. Cells packed with more particles than water at rest would hold get an extra push outward, so the water doesn’t slowly lose volume.
- Grid back to particles. Each particle reads the corrected velocity from the faces around it. Here’s the trick in the name: it doesn’t take the grid’s velocity (that method, PIC, works but smears motion away and the water turns to syrup); it keeps its own velocity and adds only the change the grid made. That keeps all the small-scale motion the coarse grid can’t represent. Pure FLIP is noisy, so the blend here is 90% FLIP and 10% PIC, and that 10% stands in for water’s tiny viscosity.
If the fastest water would move more than two cells in a step, the step is split into smaller ones so nothing tunnels through a thin wall. In 3D each of those stages touches three times as many neighbors as in 2D, which is why the grid is coarser here, and why speed mattered (more below).
Moving the tank without moving the water
The obvious way to tip a tank is to tip the grid. That’s the hard way: the walls would cut diagonally through the cells. So the simulation never moves. It runs in the tank’s own frame, where the walls and rocks always line up with the grid, and moving the tank becomes forces the water feels, the ones you feel in a car or on a roundabout:
- Tilt turns gravity: seen from inside the tank, “down” points at a different wall.
- Sliding pushes the water back, the way coffee sloshes when you start walking.
- Turning adds an outward push (centrifugal), a sideways drift of anything moving (Coriolis), and a jolt as a turn starts and stops.
Each arrow press is a short, smooth move that starts and ends at rest, so these forces are known exactly. I checked them against textbook answers: a tank accelerating sideways tilts its water surface to a slope of a/g, a tank turned on its side ends with its water against the new floor, and water in a spinning tank rises up the walls into a curved bowl.
A glowing light in the sky adds one more force. It swings from far left to far right and back every 2.5 seconds, pulling the water toward it with about a third the strength of gravity. A wave takes about 1.25 seconds to cross this tank and back, so the light keeps the water sloshing without the motion building up out of control.
A shipwreck and some rocks

The wreck comes from a painting of a beached fishing boat, pale teal paint flaking to orange rust. It’s built in code: a hull lofted from cross-sections narrowing to a raked bow, a deckhouse and wheelhouse, railings with posts missing, and a leaning mast, painted with the same brush strokes as everything else. The rocks are a reusable boulder model: give it a box and a seed and it grows a unique rock in that box, with flat facets where slate has split, layered ledges, clefts, a lean, and moss on whatever faces up.
The water meets them through signed distance fields: grids storing, at every point, how far it is to the nearest surface, negative inside. Any shape that can answer “is this point inside me?” can be baked into one, and step 3 above becomes a single lookup per particle: if a drop is too close, push it out along the direction the distance grows fastest. The mesh you see and the shape the water hits come from the same function, so they always agree.
From physics to pixels
The simulation and the drawing run on separate threads. A worker thread owns the water and steps it 60 times a second on its own clock; the page only draws, and only when the worker has sent something new:
The model-railway look is a fake tilt-shift lens. A real one keeps only a thin band of the scene sharp, which is what your eye expects of something a few centimeters away. Here the scene is drawn sharp, blurred copies are made, and each pixel picks its blur from how far it is from the tank, above, below, in front or behind, with a little extra saturation and a vignette on top.
Quieting the fans
The first version made my laptop sound like it was taking off, so I measured. The surprise: two-thirds of each step went to step 2, pushing overlapping particles apart, and only a few percent to the pressure solve. Sorting the particles by grid cell, so neighbors sit next to each other in memory, and checking each nearby pair once instead of twice made every step 35 to 40 percent faster.
The rest was wasted drawing. The screen runs at 120 frames a second, but the water only changes 60 times a second, and the page kept redrawing even while paused. Now the page draws only when the worker sends a new frame, and the worker sleeps when nothing is moving. Idle, CPU use went from about three cores to a third of one in a software-rendering test, and the water runs at real speed whatever the frame rate.
Try it
Isle of Savreen is live here, and the code is on GitHub: AndrewKL/IsleOfSavreen. It’s TypeScript with three.js, a Web Worker for the simulation and the Web Audio API for sound. Turn your sound on, click to start, then tip the tank: the arrow keys slide it, Shift with the arrows tilts it, and Q and E spin it.