Start now

Serene

Play your drawing. Add a Serene frame from the panel, draw inside it, then press the play button that appears above the selected frame: a playhead sweeps the frame at a constant rate and every stroke it crosses rings out as a sine tone, snapped to a C pentatonic scale spanning C4 to C7 by default, or any range you pick between C1 and C8.

Drawdy34

Serene

Play your drawing.

Open Serene from the extension rail and it drops a Serene frame onto the board (or offers to add another). Draw inside it, select it, and press the play button that appears above the frame. A playhead sweeps left to right across the frame at a constant rate — a wider region takes proportionally longer — and every stroke it crosses rings out as a sine tone with a short, tunable attack, then decaying into a long reverb tail.

Pitch comes from height: by default the bottom of the region is C4 and the top is C7, and the panel's Range row lets you move either end anywhere from C1 to C8. Everything between snaps to a rung of the chosen scale, so nothing lands on an interval you did not ask for no matter what you drew.

Scales

Pick one in the panel. Every scale spans the same octave range, so switching changes the colour and the number of rungs, never the range.

ScaleDegreesRows (C4 to C7)
C major pentatonic (default)C D E G A16
C majorC D E F G A B22
C dorianC D E♭ F G A B♭22
C lydianC D E F♯ G A B22
C mixolydianC D E F G A B♭22
Japanese hirajoshiC D E♭ G A♭16

A seven-note scale gives the playhead more rungs to cross in the same height, so the same drawing has finer pitch resolution and more steps in a slide.

Serene frames

Only a Serene frame plays. It is an ordinary Drawdy frame tagged with meta.serene = true, so you can move, resize, duplicate and delete it like any other frame. The first time you open the panel with no Serene frame on the board, one is added for you at the nearest empty spot around the camera and the camera flies to it. After that, the panel's Add another Serene frame button does the same.

When exactly one Serene frame is selected, a transport bar floats above its top edge: a round play button on the left and a seek track spanning the frame's width, with a knob that follows playback. Press the button to play the frame; while it plays it turns into a stop button and the bar stays pinned to the frame being played. Click anywhere on the track to jump there, or drag the knob; a drag lands when you release, whether the frame is playing or paused, and the voices under the new position pick up mid-note without a fresh attack.

What gets played

The region is the frame's bounds. Everything intersecting that region becomes ink, and each element's opacity sets the loudness of the voices it produces on a squared taper — 50% opacity is about 12 dB quieter, 25% about 24 dB — and a fully transparent one is silent. Laser pointer trails count as ink while they are visible: draw with the laser inside a playing frame and the trail sounds until it fades. The score follows the board live: drawing, moving or deleting anything inside the frame, or moving the frame itself, rebuilds the score, and while the frame is playing the new voices are slotted in ahead of the playhead so a stroke you add mid-sweep still sounds when the playhead reaches it. Shapes contribute their outline, freedraw and lines their stroke, text and images their bounding box.

Containers are staging, not content, so they stay silent: frames always, and any element you pointed at whose bounds enclose everything else in the region — a rectangle drawn around a sketch would otherwise drone its own four edges over the thing it frames. A container is only played when it is the only thing there.

Legato

Glide belongs to strokes. A freehand stroke, a line, an arrow or a laser trail is one voice: it is split into runs that are monotonic in x, and each run becomes a single oscillator that is struck once and then slides — the pitch moves as the playhead climbs or falls along that stroke's own geometry, without a new attack. Glide never crosses from one element to another; separate strokes are separate voices. Everything else — rectangles, circles, diamonds, text and image bounds — triggers discrete notes instead: every change of pitch along the outline is a fresh strike, so a circle rings as a run of plucked notes and a vertical edge as a quick arpeggio. A horizontal stroke holds one note. A diagonal glides from row to row. A circle is two arcs, one sweeping down to the bottom and back, one up to the top and back. A vertical stroke covers one column and becomes a fast glissando through every row it crosses, but only when it spans three or more rows in that column; shallower movement snaps to the single degree the stroke mostly sits on, so a diagonal steps once per column and a dot is one note. A dot is struck like a mallet: a short tap that rings down on its own while the reverb carries it.

Separate strokes still stack into chords, and a stroke that doubles back on itself starts a new voice at the turn, because the playhead has already passed that x.

Pitch breakpoints land on the column grid, so every step is a scale degree, and a stroke lands on each degree and then slides into the next one over the last third of the gap, so you hear the notes the ink crosses and a short portamento between them rather than a continuous sweep.

The region is rasterized onto a grid of the scale's pitch rows by durationSec * stepsPerSecond columns, which drives note timing and the voice velocities. Voices that start on the same column are thinned to maxVoices, keeping the outermost ones so a chord keeps its shape.

Backing track

The panel's Backing card adds a chord progression under the drawing, played by a soft triangle pad (or plucks when a chord is struck more than once). Pick a progression for the current scale, a voicing — bass only, bass plus root and fifth with the third omitted, or the full triad — and a rhythm. Balance it against the drawing with the Notes and Backing knobs in the rack. Rhythm is 1, 2 or 4 strikes per chord (a single sustained chord is held for 85% of its slot so chords breathe), or an arpeggio over a sustained bass. Up runs 1 3 5 1' 3' 5 3 1 through the triad and its octave, Down is the mirror image starting from the top, and either pattern plays twice per chord in double time. Arpeggio notes are plain sines struck with a near-instant attack. The bass sits around C2 and the chord tones in the C3 octave regardless of the melodic range.

Progressions are built diatonically from the scale, so chord qualities follow the mode: in C major I vi IV V, I iii IV V, I V, I IV or I; in dorian i III IV, i IV or i III/6 IV/6 (first inversions whose bass walks down from the tonic: C, G below, A); in mixolydian I v or I vii, where the vii bass steps down to the B♭ below the tonic rather than up; in lydian I II V, I II I II, I II, I V or I (the four-chord I II I II is the two-chord one at double time). The major pentatonic borrows the major progressions, harmonised from the full major scale; hirajoshi holds a single I.

The backing is locked to the sweep. A frame up to about 1000 px wide plays the progression once across its duration; every further 1000 px adds another pass (nearest multiple, minimum one), so a 2000 px frame plays it twice and a 1500 px frame twice at a brisker pace. Because the chords are ordinary voices in the score, they follow speed changes, seeks and loops exactly like the ink.

Panel

The extension rail opens a transport panel with play/stop, a speed slider (px per second — this is what sets the duration), attack (0 to 300 ms, default 20 ms — how long each voice takes to reach full level), a Notes level for the drawn voices, a Backing level that appears only while a progression is selected, and reverb, a Loop toggle that wraps the sweep back to the start without cutting any tails, and an Add Serene frame button with a count of the frames on the board. Every knob and the scale are remembered across sessions in the driver's key/value storage, which is why the manifest asks for the storage permission.

Dry and reverb sum into a mix bus that runs through a limiter before the volume control: a dense passage stacks five voices per column on top of the tails of everything the playhead just passed, which sums past full scale and clips. The limiter is a hard-knee compressor at -3 dB with a 20:1 ratio, a 2 ms attack and a 180 ms release, followed by a fixed LIMIT_TRIM of 0.821 that cancels the makeup gain DynamicsCompressorNode applies on its own — without it the limiter would make everything below the threshold 1.22x louder instead of only catching the peaks. Volume sits after the trim, so turning up cannot drive the limiter harder.

Measured offline in Chromium against the real graph: a single voice and a five-voice chord come through untouched (0.169 and 0.681 peak either way), while a 3-second run of five voices per column peaks at 1.168 with hundreds of clipped samples unlimited, and 0.796 with none through the limiter.

Audio lives in the panel's webview, which runs in a sandboxed opaque-origin iframe. Browsers require one real click inside that frame before a page may make sound, so the first play has to come from the panel's ▶ button. After that, the on-canvas play button starts playback on its own.

Develop

pnpm install
pnpm dev          # serves /built.drawdyx for the "Add extension dev server" palette command
pnpm typecheck
pnpm build        # dist/drawdy-serene.drawdyx, also copied into frontend/public/extensions