A small animated icon looked fine on an emulator and stuttered on a real phone — because cheap in isolation isn't cheap in aggregate.
Every forest in the directory has a glyph — three concentric
rings, counter-rotating; a tracer dot orbiting the inner one. It
carries the same visual idiom as the breathing rings in
Constellation and the orbital rings in Folk. The system is the
icon, miniaturised.
A single glyph runs smoothly on any device. The trouble was that
the directory shows several at once — every visible forest in a
scrolling list. Five or six glyphs, each animating sixty times
a second.
On an emulator it was fine. On a real phone it stuttered.
Before — five glyphs animating at once on a physical phone
What was actually happening
The animation itself wasn’t the problem; the render path under it
was. The glyph was drawn through a vector layer that lives on the
native side of a bridge — every frame, every rotation, every ring
sent a small message across that bridge to schedule a redraw. One
glyph at sixty frames a second is fine. Five glyphs, each carrying
three independent rotations, isn’t — around twenty bridge messages
per frame, all queued on the same thread the screen uses to paint.
The animations stayed fast in isolation. The rendering thread
couldn’t keep up. Frames dropped in twos and threes — exactly the
cadence the eye reads as stutter.
The fix
The glyph was rewritten to draw directly to a GPU canvas instead.
Same three rings, same counter-rotation, same tracer dot — but
they live on a single surface that the GPU repaints in one pass.
No bridge messages, no per-ring scheduling, no per-frame
indirection. The work that used to multiply linearly per glyph
collapses into a single canvas update.
The animation runs from one shared clock on the UI thread; each
ring reads the clock and computes its own angle in a worklet. No
timers, no per-component animation drivers — just math, on a
thread that doesn’t have to ask anyone else to schedule a redraw.
After — same animation, no dropped frames
The other half
The glyph fix was half the work. The directory itself has on the
order of twelve thousand forests; mounting every row up front
isn’t an option, glyph cost or not. The list is windowed — only a
few viewports of rows are mounted at any moment, and as you
scroll, rows further off unmount while new ones come in. Scroll
position is preserved by the math, not by force.
It’s the same shape of solution a personal forest already uses
for its message canvas, just applied to a directory rather than a
chat thread — the same kind of windowed
scroll.
The lesson
Visual fidelity is one budget; render-path cost is another.
Something that’s smooth in isolation can be laggy in aggregate
not because the animation is bad, but because the path it takes
to the screen multiplies poorly.