All work

Chorus

Giving shared music a sense of material and motion

Context
Personal project
Scope
Music search, Web Audio behavior, 3D rendering, mobile composition, and image export
Stack
SvelteKit, Svelte 5 Runes, TypeScript, Threlte, Three.js, Tailwind CSS, iTunes Search API

A visual card still needs to work on a phone

I wanted to turn a song, album cover, and short message into a vertical sharing card. Search, audio preview, 3D rendering, and saving needed to form one mobile flow.

This required handling browser audio policies, capturing WebGL content, and adapting image saving to different devices.

Connect rendering to playback and export behavior

Search and card state

SvelteKit and Svelte 5 Runes connect iTunes search and track selection to the studio. The cover, title, artist, and custom message are reflected in one composition.

Audio-driven material and motion

Threlte and Three.js render a reflective vinyl record. Web Audio frequency data drives a bass-responsive visualizer, while playback controls rotation and visual state.

Audio context creation, resume, and playback begin from a user gesture for mobile browsers. Leaving the screen pauses audio, closes the context, and cancels the animation frame.

Capture WebGL and text together

The WebGL renderer preserves its drawing buffer and html-to-image captures the canvas with the surrounding card. A saving guard prevents duplicate requests and a brief render delay precedes capture.

Desktop export triggers a download; mobile export opens the image for saving. Link sharing prefers the Web Share API and falls back to copying the URL.

Search, listen, compose, and save in one deployed flow

Chorus connects music search, audio preview, reactive 3D rendering, message editing, and vertical PNG export in a publicly deployed service.

The implementation addresses browser-specific playback, sharing, and saving behavior alongside the visual experience.

Trace

Live
Public service deployment
Web Audio
Gesture-driven playback and visualization
PNG
Capture WebGL and card content together