Sand Garden.

Make a pattern, watch one form, or leave the garden playing.

Independent project · Product design and AI-assisted engineering · Alpha testing
Open Sand Garden
0:00 0:00
Auto tending the Stillness board in the current alpha. Choose a tool to begin working on the same sand.

I designed and built a browser-based sand garden. It began as something visitors could spend time with on this portfolio, then became an app of its own.

Watching was the starting point. Raking asked more of it.

A garden could look pleasing while its rake followed an impossible path, rushed through a gesture, or left old marks behind. Letting someone work the sand meant making the tool, its motion, and the surface agree.

The project is in alpha testing. The decisions here come from building it and using it on my own computers, phones, and TV.

Project details
My role
Product design, interaction, material direction, and AI-assisted engineering.
Built with
React, Three.js, and TypeScript. Began inside this Astro portfolio; now a standalone app.
Testing so far
Personal device testing and controlled browser checks. No external users yet.
Pattern and tool

The rake had to be able to draw the pattern.

The first patterns were drawn as neighbouring curves. At a tight turn, they could look convincing while asking different teeth of the same rake to move in opposite directions.

Early two-dimensional rake study showing the paths of four teeth around stone islands and their directions through a turn
The early 2D study exposes the tooth paths before they drive the 3D garden.

I asked to see the mathematics in two dimensions before taking the pattern further. The AI-assisted build had produced paths that looked plausible, but the tool and its marks had been worked out separately.

One moving rake became the source of every groove. All four teeth share a position and orientation, and the same path drives the 2D study and the 3D scene. Checking the teeth through a turn made the correction visible before more material work hid it.

That rule governs the tool. It does not force someone’s drawing to become a perfect circuit. Auto uses composed paths; a person can stop a stroke wherever they want.

Learn more about the geometry check
Drawing and motion

A delayed cursor was still a hurried rake.

Drawing directly made a quick gesture turn into a quick rake. Adding a delay only made it wait before rushing through the same movement.

0:00 0:00
Draw a path, then let the rake follow it. The waiting line remains visible ahead of the tool. Recorded in the current desktop app.

The hand now describes a path while the rake follows it at a controlled pace. The waiting curve is smoothed and remains editable. It is the route the rake will take, rather than a second line painted on the sand.

A test with the same gesture at three input speeds exposed the old timing problem and checked the replacement. The rake’s peak speed stayed at its configured limit across all three.

The surrounding controls follow the same approach: handles on the line, tools beside the board, and six composed gardens to begin with. Someone can also leave Auto running without drawing anything.

Working the sand

A new stroke should replace the old one.

Drawing fresh grooves on top of old grooves made the sand behave like paper. Crossing a stroke needed to change what was already there.

0:00 0:00
A crossing pass replaces the earlier grooves across the tool’s width, then leaves its own marks.

Each pass replaces the swept strip beneath the rake before adding its new grooves and ridges. Shallow height changes give the marks some displaced sand without turning the project into a full sand simulation.

This also shaped the interaction. There is no undo stack: another pass can change a mark, and the smoother can work it away. Photo mode and replay preserve a moment or its process without changing that rule.

Auto works on the same surface. It keeps making new compositions over existing sand instead of clearing the board between patterns. Switching to a manual tool keeps what is there.

Phones and performance

A faster desktop test did not fix the phone.

Testing on my iPhone exposed slowdown when the rake touched the sand. A Pixel also lost its graphics context. An optimization that looked better in desktop Chromium did not settle either question.

The upload path still sent large buffers for small changes. Packing only the changed area cut the recorded short-contact update from 172 ms to 1 ms in a Mac WebKit test. Output comparisons checked that the faster path left the same sand behind. Retesting on my iPhone confirmed that raking worked well there.

Lowering the resolution was less useful. It passed a desktop fixture, still failed on the Pixel, and made the sand visibly worse. Full-resolution textures and antialiasing returned; the lighter mode instead reduces terrain detail, filtering, shadows, and particles.

The Pixel failure remains unresolved. The app now offers a restart that preserves the garden if graphics stop. TV testing also caught unnecessary rendering above the screen’s native density, which could waste work even with the rake lifted.

See the graphics recovery flow
0:00 0:00
Current app, with graphics loss deliberately triggered in a desktop browser. This demonstrates recovery, not the unresolved Pixel failure.

Graphics can fail before the first frame as well as during a session. Renderer creation, rendering errors, and a startup timeout now lead to a recovery state instead of leaving the loading message indefinitely.

Restart in Optimized restores the sand and camera. The app also keeps photo and replay state across a failed retry. Reload is a separate action because it begins a new visit.

Where it is now

Ready to watch and rake. Still in alpha.

The garden can run on its own, accept a hand-drawn path, and keep changing the same surface. Those interactions now belong to one app rather than separate studies.

Raking worked well on my iPhone after the upload change. Testing on the TV exposed a different problem: it could be slow even when the rake was lifted. Those observations helped separate the cost of changing the sand from the cost of drawing the scene.

No one else has used it or given feedback yet. My own testing does not tell me whether a visitor understands the first stroke or wants to come back.

That is the next useful test: watch someone begin on their own phone, see where they hesitate, and find out whether they choose to rake or simply watch. Longer display sessions need testing too. The unresolved Pixel failure remains part of that work.

The original idea was to give visitors somewhere to spend a little time. There is now a working garden to put in front of them.

Learn more about the standalone app
Project history

Project history

One rake defines the path, moves at a controlled pace, changes the sand, and sends that change to the screen.

September 5–7, 2026

One thread · 4 decisions

Follow a stroke from the rake to the screen.

  1. 01One pose defines all four teeth.Learn more

    The tool can trace the pattern. Next, control how it gets there.

  2. 02The rake follows distance, not pointer timing.Learn more

    A moving rake affects the whole swath beneath it.

  3. 03Each pass replaces earlier grooves.Learn more

    Those continuous edits need small, predictable screen updates.

  4. 04Send the changed area, not a full surface.Learn more
Explore the decisions in chronological order.Start with the earliest note