The pool this page boots with is about 27,000 particles, simulated in compute shaders on your GPU. The slimes are soft bodies solved on your CPU. The smoke is a third simulation, a grid fluid running in WebGL. The three of them share exactly one thing: a grid of 8-pixel cells that says solid or not. Draw a wall and all three feel it.
Everything here was pulled from Sluice, the little mining game that lives on this site, where the water floods dug tunnels, the smoke is diesel exhaust, and the slimes are rare buried creatures that cushion a bad fall. This page is those three systems with the game stripped away and a pencil put in your hand.
The water
The water is the game's fluid solver, byte for byte. It is an MLS-MPM sim (Hu et al. 2018; nialltl's guide is the friendliest walk-through): particles splat mass and momentum onto a grid, the grid resolves pressure, particles gather velocity back and move. All of it runs as WebGPU compute, advancing in fixed 1/120-second substeps (several per frame, so the physics is identical at 60 and 120 Hz), and the grid work only touches 16-by-16-cell blocks that contain water, so the cost scales with how wet the box is. The page allows 100,000 particles on desktop and 40,000 on a phone.
A min-separation pass shoves over-packed particles apart each substep, because particle water that is allowed to over-pack builds pressure spikes and pops. The kernels also carry a per-particle sleep system, and in practice the shipped tuning barely uses it: the weakly-compressible pressure model keeps a settled pool faintly breathing (I measured a still pool for half a minute, and not one particle dozed off). The real savings are the sparse grid and the fact that the page parks the whole loop when the toy scrolls off screen. The pressure tuning follows saharan's water demo.
The WebGPU engine also had a foundational plumbing bug: its live damping and motion controls changed old JavaScript scalars while the shader kept reading values frozen in a material table at boot. The effective 0.992 damping and 0.97 motion scale were applied about 186 times per second, leaving less than 0.1 percent of carried momentum after one second. That is why a stream fell slowly and a spill spread like honey. The repaired demo leaves body momentum undamped, uses only a trace of grid smoothing, and gives airborne drops room to accelerate. The pressure limiter, density cap, anti-clump pass, travel cap, and swept collision still do the stability work.
The blue water is a surface reconstructed from overlapping particle fields, not one visible dot per simulated particle. Interior particles are deliberately hidden because they supply mass and pressure beneath that surface. Fast spray exposed a hole in the handoff: the old droplet pass guessed visibility from a neighbor count taken before motion, so a sheet could count as supported, spread apart, and then miss both the surface threshold and the droplet pass. The droplet pass now reads the finished density field at each particle and draws a compact mark aligned with its velocity wherever the surface did not cover it. Thin pours read as narrow strands instead of round confetti. The fused field itself uses compact support instead of wide circles around every particle, and both render paths test the collision terrain before painting a pixel. Water therefore stays continuous without swelling at ledges or appearing inside a wall. A separate half-second sweep retires tiny isolated clusters only after they slow down or spend four seconds without rejoining water, so invisible strays no longer live on as permanent compute work.
The water control no longer pretends that viscosity and playback speed are the same thing. Version 4.6 keeps gravity, simulation time, particle size, and the surface renderer fixed. Its first stage cancels tiny disagreements between slow neighboring cells, including a puddle only one grid cell deep. Its second stage smoothly shortens the shared momentum of body water only after that water has slowed. An impact crosses the speed gate immediately, and density-separated drops keep the raw solver through their whole arc. Very calm therefore settles a shallow sheet decisively, fluid leaves a useful slosh, and very lively lets the tail travel longer. The page opens at 42 percent, near the lower end of fluid. No endpoint freezes the simulation. The old 25-sample goopy blur and its lattice-forming physics are gone, so moving the control cannot resize the water.
The ugliest bug this page found was not pressure at all. A fast slime could throw particles across the box in one frame, sometimes through a wall. Its boundary arrived in pixels per second, while the water grid stores cells moved per 1/120-second step; one missing conversion multiplied the shove by about 300. The solver now interpolates the moving outline through each substep, caps particle travel before updating position, and checks the whole path to the new position for a wall. The old endpoint-only check was how a particle could clear a barrier and look legal on the other side.
Touching slimes found a different bug. The page ranked the wettest boundaries every frame, so tiny changes in water contact made the same three slimes trade GPU slots. Inside an overlap, the grid let the last slot win and particle collision projected through the rings one at a time. Water could fill a cavity until the order changed, then snap back. Slots are now stable, and touching outlines are solved as one exterior silhouette: one grid condition and one particle correction, independent of array order.
This is the one engine with a hardware requirement: no WebGPU, no water. The smoke and the slimes run anyway.
The slimes
The visible slime is one continuous surface. Its color, depth, caustics, and highlights come from the live silhouette and bulk deformation. None of the points or links used by the solver are drawn. The result reads as one piece of translucent gel, even while it stretches, rolls, or flattens.
Under that surface, XPBD distance constraints (Macklin, Müller, Chentanez 2016) hold the body together, a shape-matching pull (Müller et al. 2005) remembers its rest silhouette, and a gas-pressure term holds its area. That last term is why a squashed slime bulges at the sides instead of quietly losing half its volume.
Poke now attaches to a continuous coordinate on the slime's boundary. The original click offset is preserved, and that surface coordinate receives the pointer's full displacement at every physics step. There is no virtual proxy and no maximum handle speed. Collisions can squash the rest of the body, but they cannot slow the hand or save up a delayed pull. If the hand crosses a wall, the hand wins for the duration of the gesture. Release returns authority to collision and converts the latest real pointer samples into the slime's velocity.
The same rewrite adds the control seam the game will need later. A slime can now receive an intent made of bulk motion, a jump impulse, smooth pose scales, and a wobble phase. Those controls never refer to an interior point or a spring. A round slime, a triangle, an irregular buried blob, and a later authored creature can therefore share one movement and behavior layer.
Slime-on-slime contact runs inside the same substep loop through a count-sort spatial hash, the approach in Macklin et al.'s unified particle physics. Running contact per substep instead of once per frame is the difference between blobs that stack and blobs that jitter and merge. The game learned that the hard way.
Drop one in a pool and it floats. Each frame the toy builds a small map of the water and reads each body a pool-local waterline, ending the search at a wall or an air gap. Submerged volume trades gravity for lift and takes drag toward the local flow. Currents carry a floater, a waterfall shoves one down, and a plunge damps itself out. Net lift runs about a quarter beyond gravity, so the slimes ride mostly submerged, like dense gel.
The smoke
The smoke is the oldest method on the page: a classic Eulerian grid fluid in WebGL, based on Pavel Dobryakov's much-copied demo. Velocity advects itself, 25 Jacobi iterations per frame make the field incompressible, vorticity confinement restores the curl the coarse grid smears away, and what you actually see is a dye field carried along by all of it.
The drawn walls are rasterized into its obstacle mask whenever they change, and so are the slime outlines and slow, dense water. The landing scene's vent feeds the field continuously, thirty small injections each second rather than one oversized puff every half-second. Its plume will split around a blob that is wobbling in real time and bank off the surface of a pool. Fast water works differently: the toy averages its speed in 8-pixel cells and feeds the strongest falling cells into the smoke's velocity field, so a waterfall drags a plume down with it instead of cutting a square hole through it.
One dumb grid
There is no shared physics library underneath. There is one flat array of 8-pixel cells. The water kernels read it packed as a bitmask, the slime solver probes it point by point, the smoke receives it as a texture. Drawing a line edits all three simulations at once, which is why the page feels like one world instead of three separate demos.
The direct couplings between engines are thinner, and here is the honest list. Water lifts and drags each slime across its submerged volume. The eight wettest slime outlines are handed to the water solver as moving boundaries, resampled to 20 vertices each, so rain piles on a slime and slides off as it wobbles, and a raft of floaters visibly displaces the pool. Selected outlines retain stable solver slots, and touching outlines become one exterior boundary. Smoke banks off slime silhouettes and slow water; fast falling water pushes the smoke down. Beyond the eight tracked outlines, water still falls straight through a slime.
Worth trying
- Slide gravity to zero while the falls are running. Water already in flight keeps its momentum and drifts into floating blobs; the trickle becomes a chain of pearls. Then try 2g.
- Set time to 15 percent and poke the pool, hard. The crown splash is the whole reason the slider exists.
- Draw a bowl under the spout, let it fill, then erase the bowl's floor. Erasing wakes the sleeping water above it.
- Open the spa scene and drop a slime from the very top of the box. It plunges, surfaces, and bobs its way calm.
- Choose poke and whip a slime across the box. The held surface stays with the pointer, then the release turns the last hand motion into a throw.
There is no framework and no build step; the page is static files, and view source shows all of it, including the mistakes. If you want the version with fuel and consequences, it is called Sluice and it is free forever.