Skip to content
← All starter build updates

Creative starter

Oscillator

A browser music sketchbook for building patterns and exploring sound.

Live on VuraUpdated

Saved/custom pattern identity, guarded storage failure and numerical controls match the instrument state. The playable sequencer appears much earlier on mobile; audio ownership and guide containment pass.

Oscillator: actual released desktop product view after the shared typography refinement.
Preview captured · Explore the product ↗

Build phase

  1. PlannedComplete
  2. BuildingComplete
  3. Local checks passedComplete
  4. In reviewComplete
  5. Live on VuraCurrent phase

This tracks the recorded release phase. The checks below include later findings and ongoing refinements.

Product scope and intended patterns

The following describes the intended reference. Features are not a verification claim; recorded check results below define what has actually been tested.

Client-rendered application served from explicit static route entries; browser-local state, not a shared server database.

Learning journal

How to read this starter

Oscillator is a browser music studio that demonstrates global pattern state, Web Audio lifecycle cleanup and routeable studio/build pages.

Source map

Audio engine

src/audio/engine.js

A single startGeneration and startingPromise coordinate async AudioContext startup.

Lifecycle tests

src/audio/engine.lifecycle.test.js

Tests cover duplicate starts, stop-before-resume and dispose-before-resume races.

Studio state

src/state/studio.js

Signals store pattern, playback state, current step and user-facing audio status.

Pattern identity regression

src/state/studio.test.js

Tests restore saved presets, identify custom edits and exercise denied save/reset operations.

Sequencer grid

src/components/Sequencer.jsx

The step ruler owns its grid and the playhead is visible only during playback.

Responsive application stylesheet

src/styles.css

Mobile steps and ruler share four columns; transport controls retain their numeric readouts.

Code patterns worth copying

Coalesce async audio starts

src/audio/engine.js

Repeated Start clicks before AudioContext.resume() resolves share the same promise instead of creating duplicate intervals.

if (startingPromise) {
    audioStatus('Audio starting.');
    return startingPromise;
  }
  if (!isAudioSupported()) {
    browserError('This browser does not expose Web Audio. Try a current Chromium, Safari, or Firefox build.');
    return;

Prove pending starts can be cancelled

src/audio/engine.lifecycle.test.js

The regression test models the exact race: the user stops playback while browser audio permission is still resolving.

const pendingStart = startEngine();

Only show the playhead while playback is active

src/components/Sequencer.jsx

The stopped studio keeps selected steps visible without leaving a fake current-step highlight on the grid.

isPlaying() && currentStep() === step && 'playing',

Derive preset identity from the current patch

src/state/studio.js

The restored pattern, not a hard-coded first-preset id, determines the selected preset. An edited patch has custom identity.

export const currentPresetId = computed(() => presets.find((preset) => JSON.stringify(preset) === JSON.stringify(pattern()))?.id || '');

Real issues and fixes

A sequencer ruler needs its own grid

Problem
The old step header mixed a blank track cell and sixteen numbers in one parent grid, then hid the final child with CSS.
Fix
Render the step numbers inside a dedicated .step-numbers element and let CSS give that child the same column count as the step buttons.
Proof
The browser smoke checks the desktop sixteen-column ruler and the mobile eight-column wrap.
Takeaway
A visual alignment bug can be a data-structure bug in the markup, not only a CSS spacing bug.

Double-start race created multiple schedulers

Problem
Two quick start calls could both await resume and then install intervals.
Fix
A shared startingPromise coalesces callers, and startGeneration invalidates any pending resume when stop or dispose runs.
Proof
Lifecycle tests assert one interval for simultaneous starts and zero intervals when stop/dispose wins the race.
Takeaway
Long-running browser resources need idempotent start and explicit cancellation, even in a demo.

A restored patch should not claim the wrong preset

Problem
Reloading a saved patch could highlight Brass Grid regardless of its contents; custom edits and saved status were conflated.
Fix
Compute preset identity from exact pattern contents and track the saved serialized snapshot separately. Catch storage writes/removal so audio editing can continue in-session.
Proof
Studio regressions restore Slow Bloom, preserve custom identity after edits/reload, and verify save/reset do not throw under denied storage.
Takeaway
Preset identity, current edits and persistence success are separate facts.

Reflow step controls and their ruler together

Problem
Sixteen 44px sequencer targets cannot fit one 360px row; changing only the buttons leaves the ruler misaligned.
Fix
Give both .steps and .step-numbers four equal columns at the narrow breakpoint and retain 44px step minimums.
Proof
The stylesheet pairs the grid selectors and preserves the step minimums; browser regressions cover the narrow sequencer and ruler.
Takeaway
Reflow related controls and labels together instead of shrinking targets.

What went smoothly

  • The fake AudioContext test harness made browser-only race conditions testable without playing sound.
  • Keeping pattern state separate from the engine let the UI mutate tracks while the scheduler reads the latest pattern.
  • The existing audio scheduler reads current pattern state, so saved/custom identity needed no scheduler redesign or new audio resource.
  • At 360px, the sixteen steps and their ruler reflow together into four-column groups, preserving 44px targets without altering audio state.

Boundaries to preserve

  • The studio runs entirely in the browser; it does not stream audio or save patterns to a server.
  • Audio support depends on the browser exposing AudioContext.

Verification record

Build journal

  1. · Live on Vura

    Desktop hero heading exception closed after targeted release

    The historical 56px finding is retained. A narrow stylesheet repair now caps the released home heading at 48px while preserving the 32px mobile heading, studio controls and audio state. Fresh production route captures and navigation checks identify the new source and deployment checkpoint.

  2. · Live on Vura

    Shared typography release captured from production

    Published shared typography and responsive control refinements are tied to the exact live deployment and source revision. Actual released captures replace the previous gallery preview; existing product behavior and source-grounded lessons are retained. The desktop home hero measures 56px, exceeding the 48px heading target; this styling exception remains open.

  3. · Live on Vura

    Product-depth iteration released and exercised

    Saved/custom pattern identity, guarded storage failure and numerical controls match the instrument state. The playable sequencer appears much earlier on mobile; audio ownership and guide containment pass.

  4. · Live on Vura

    Reviewed source ready for release verification

    Unique product identities and deeper workflows are implemented and independently reviewed. Existing releases remain accessible while new source is published and fresh deployments are exercised.

  5. · Live on Vura

    Product-depth iteration in progress

    Repairing denied-storage feedback and saved-pattern identity, with the sequencer earlier on mobile. The current source and deployment links still identify the previous verified release.

  6. · Live on Vura

    Live corrections verified after independent follow-up

    The studio has a genuine16-step ruler, compact track controls and an honest stopped playhead. Live step toggling passed a clean browser retry.

  7. · Live on Vura

    Design refinement released for live workflow review

    The studio has a genuine16-step ruler, compact track controls and an honest stopped playhead. Live step toggling passed a clean browser retry.

  8. · Live on Vura

    Independent live design review started

    The existing public app remains available while a new design review examines hierarchy, typography, navigation and workflow clarity. Actual findings and before/after repairs will be recorded as they are verified.

  9. · Live on Vura

    Hosted reference rechecked after documentation review

    Public source, green CI and the live app were checked together after the clean-machine documentation pass. Lessons and limitations now reflect the implemented code rather than pending pre-release work.

  10. · Live on Vura

    Final source and CI checkpoint verified

    The public source and build reference match the released learning journal, and the source workflow is green. The demo remains live at the same verified deployment; README and CI-only refinements did not change application code.

  11. · Live on Vura

    Clean-machine browser setup documented

    The README now installs the locked Chromium engine after npm ci and explains the Linux system-library prerequisite. The hosted application is unchanged; code examples, repair notes and boundaries remain source-backed.

  12. · Live on Vura

    Public template and Vura release

    The reviewed implementation is available from its independent repository and real hosted URL. Detailed code examples and actual repair notes are linked below.

  13. · In review

    Design review cleared; release verification underway

    The local implementation is complete and revised visual evidence has passed review. Source and demo links will appear only after publication and hosted verification.

  14. · In review

    Local implementation ready for review

    The first implementation is available locally. It is not yet a published source reference or a verified Vura deployment.

Implementation lessons

Known limitations

Remaining work and blockers

No release blockers are recorded for this snapshot.

Using this as an agent reference

Read the public README and BUILD.md, reproduce the recorded checks and inspect the source before adapting the starter. A live demo, where available, provides separate deployed evidence.