Home Shipped · May 2025

Side project — 01

What if a gradient could feel like a voice?

The idea

This is a conversational voice UI I prototyped in SwiftUI. The background is one animated MeshGradient: a 4×4 mesh grid whose control points drift on sine and cosine curves, so the surface seems to breathe the way a voice might.

It comes down to a single view: sixteen points re-animated every frame, with each voice's palette blended across them:

MeshGradient(width: 4, height: 4,
  points: animatedPoints(t),
  colors: blend(prev, card, fade))

Two layers

That mesh is only the base. The look is two layers: the mesh underneath, and a curtain of light over it.

The curtain is a blurred sheet of color, screen-blended so it only ever lightens. It carries a diagonal highlight and softens the mesh into a frosted-glass haze:

mesh.overlay {
  curtain
    .blur(radius: 8)
    .blendMode(.screen)
}

Switching voices

You switch voices by swiping the card: Aurora, Blaze, Echo. Each carries its own palette, and the colors cross-fade as you move between them. Frosted-glass controls, aurora stripes drifting across the top, and a soft reflection underneath finish the look.

The Expressivity dial scales all of this at once. At Subtle the glow stays faint and the stripes soft; at Vivid the colors saturate and the highlights sharpen.

Under the hood

The orb started as a SwiftUI prototype; I rebuilt it for the web with Cursor and Claude Code, porting the mesh math point for point until the web version moved the same way the original did.

The motion comes from a "voice pulse", a number that swells and flutters over time, the way a voice does:

func pulse(_ t: Float) -> Float {
  0.7 + 0.3*sin(t) + 0.2*sin(6*t)
}

Every frame, each point is nudged a little by that pulse, which is what keeps the surface alive:

let amp = 0.06 * pulse(t)
points[k] = home[k] + amp*drift(t)

Try it

The panel on the right is that same prototype, re-created in WebGL and running live in your browser, with the same mesh math, gestures, and transitions. Drag it, change the voice, and pull the speed and expressivity sliders to watch the wave respond.

Vibe-coding makes prototypes like this fast and fun to build. But a solid foundation still matters: the complex interactions and the visualization only come to life once you understand what is happening underneath.