← Case Studies

Squishy Butter

Press and hold a stick of butter and watch it sink. Real soft-body physics in your browser — no install, no app store. 31 collectible skins, each with its own feel, sound and certificate of authenticity.

Three.js · GLSL · WebGL2 · Vite · Supabase · Web Audio · 2026 · In development

The Idea

Almost every squishy thing on the web is a scale animation. The object shrinks toward its middle and springs back — and it feels like rubber, because a spring is what rubber does.

I wanted the other sensation: a fingertip actually sinking into something soft. The material pushing back when you jab it, giving way when you lean slowly, holding the dent for a beat after you let go and then easing back rather than snapping.

That's a materials problem, not an animation problem. You can't get there with CSS. It needed a real deformation model running on the graphics card — and once the feel existed, everything else got built around it.

Try It

Click and hold — a quick tap only reaches about a quarter of the depth. The longer you press, the deeper it goes.

squishy-butter.vercel.app

Open full screen ↗

Mobile Experience

It's built for touch first — press and hold with your thumb, drag to plough a groove, pinch and spin to look around it. The whole thing runs in the browser, so there's nothing to install.

9:41

Live — press and hold

Open full screen ↗

Why It Feels Right

Four decisions do most of the work, and each one is something you notice in the first three seconds without being able to name it.

It Sizes Itself to Your Device

Each simultaneous dent costs memory on the graphics card, and the budget varies wildly — a desktop has plenty, plenty of phones have a fraction of it. Ask for more than the device has and the shader silently fails to compile, which renders as nothing at all. The butter just never appears, and it looks like a loading bug rather than a graphics one.

const budget = gl.getParameter(gl.MAX_VERTEX_UNIFORM_VECTORS) || 128; const affordable = Math.floor((budget * 0.45) / 2); return Math.max(6, Math.min(48, affordable));

The shader asks the device what it can afford before deciding how much to use.

So it asks first, then compiles itself to fit — up to 48 simultaneous dents on a laptop, scaling down on weaker hardware. One codebase, no device detection, no separate mobile build.

31 Skins, Zero Image Files

Marble veining, zebra stripes that taper and fork the way real ones do, cheetah rosettes, wood grain that crowds and spreads along the ring, tie-dye, denim, camo, gold foil. Every one is drawn at runtime in code — there isn't a single image file in the project.

Which means the download stays tiny no matter how big the catalogue gets, and adding a skin is writing a function instead of running an art pipeline. That's the only reason a catalogue this size was practical for one person.

Skins aren't just colours, either. Each has its own physics — Frozen barely yields until you warm it up with your finger, and it warms where you touch it, so one end can soften while the rest stays solid. Each has its own sound, generated live and fed the dent's depth and speed every frame so it tracks what your hand is doing. And each has a rarity, a supply cap, a serial number, a written provenance story, a certificate and a trading card.

A Few Things Under the Hood

What Was Hard

Getting the feel right was mostly deleting things that were subtly wrong. A spring on release. A dent that stayed the same size no matter how hard you pressed. A drag that slid one divot around instead of ploughing a groove. Each was defensible on its own, and each one made the butter feel like a different material than I wanted.

The worst bug looked completely correct in the code. Dragging is meant to leave dents behind your finger, and the spacing check measured the gap between the cursor and the active dent — except that dent eases toward the cursor every frame. I was measuring the distance between the cursor and something chasing the cursor, so it never grew large enough to trigger, and below a certain speed dragging left no trail at all. Reading it, it looked fine. Simulating the loop outside the browser found it in one pass. That lesson stuck harder than the fix did.

Where It Stands

The engine works and the catalogue is finished: 31 skins with individual physics, sound and presentation, running at frame rate from laptops down to phones, in roughly 7,000 lines.

What's left is the shop. Accounts, server-side ownership and checkout are designed and partly written but not yet connected — that's the next build, and it's the difference between a very good toy and something people can own.

Tally was hard because four platforms and two payment systems had to be correct at once. This one is hard in a narrower way: the whole product is a feeling. It either lands or it doesn't, and there's nothing to hide behind.

Link Preview

Squishy Butter link preview image
squishy-butter.vercel.app

Squishy Butter

A stick of butter you can squish, running real soft-body physics in your browser. 31 collectible skins, each with its own feel, sound and certificate.