Orbifold is a Rust desktop prototype for microtonal MIDI performance, Lumatone-oriented tuning workflows, and loop-based composition.
The current UI is rendered with Operad. Orbifold is still a prototype: the single-clip recording and piano-roll workflow exists, while the DAW-like multi-track arrangement is still being wired to real project state.
- Operad desktop interface
- Selectable
cpalaudio output with reconnect controls midirMIDI input selection with reconnect controls- Persistent settings in
orbifold_settings.txt - Saved/opened project paths are remembered as recent projects
- Recent project rows can reopen or forget saved/opened projects without deleting the project file
- Missing recent project files are marked in the left session strip and can be forgotten without restarting
- Project save/load for the current scale, Lumatone mapping, synth settings, transport, and clip
- Dirty project autosave to
orbifold_autosave.orbifold, cleared after a successful save or return to a clean project state - Window title reflects the open project name and unsaved state
- Resizable workspace splitters for the left browser, clip panel, right panel, piano roll, and the Assets/Scales browser split, with layout reset and persisted panel proportions
- Right-panel Settings mode for UI zoom, workspace visibility, layout reset, settings save, and device setup access
- Scala
.sclscale loading, recent/library scale list, root note, and base-frequency controls - Lumatone
.ltnpreset mapping with preset colors and active-key display - MIDI/scale inspector showing channel, note, mapped key, scale degree, frequency, and cents from root
- Performance recorder with looping transport, overdub, metronome, quantize-on-record, clip quantize, and clip view
- Piano roll editing with pitch lanes, beat grid, note creation, note selection, delete, duplicate, nudge, transpose, resize, velocity editing, undo, and redo
- Polyphonic synth with command-queue audio control, output mute/metering, master gain, waveform, attack, release, drive, low-pass filter, delay, and soft clipping
Orbifold is not yet a finished DAW. The current usability focus is to make the app reliable, honest about incomplete workflows, and safe to run on machines without a full audio/MIDI setup.
Known current limitations:
- The visible multi-track arrangement is still ahead of the project model.
- Some controls are present as workflow scaffolding while deeper behavior is still being implemented.
- The app can launch without an audio output. Sound-producing controls such as A4 are disabled until an output is connected, while All Off remains available as a panic/reset action.
- If audio or MIDI setup is missing, the right panel opens directly in
Devices/Setup mode with a
SETUP REQUIREDsummary instead of leaving the setup path hidden in the control panel. - Visual layout is actively changing as the Operad integration matures.
See docs/known_limitations.md for the fuller tester-facing limitation list.
Orbifold has a browser build for GitHub Pages:
./scripts/build-web.sh dist
./scripts/check-web-dist.mjs dist
python3 -m http.server 4173 --directory dist
./scripts/check-web-layout.mjs http://127.0.0.1:4173/
./scripts/check-web-smoke.mjs http://127.0.0.1:4173/
./scripts/check-web-live.mjs https://<user>.github.io/<repo>/
./scripts/check-web-layout.mjs https://<user>.github.io/<repo>/
./scripts/capture-web-visuals.mjs https://<user>.github.io/<repo>/
./scripts/check-web-manual-devices.mjs https://<user>.github.io/<repo>/ --preflight
./scripts/check-web-manual-devices.mjs https://<user>.github.io/<repo>/
./scripts/check-web-manual-report.mjs reports/
./scripts/check-web-parity-status.mjs reports/ --url https://<user>.github.io/<repo>/
./scripts/check-web-parity-gate.mjs https://<user>.github.io/<repo>/ --report reports/
./scripts/check-web-parity-complete.mjs reports/ --url https://<user>.github.io/<repo>/The layout check launches headless Chrome at compact, desktop, high-DPI, and 4K viewports and verifies canvas coverage, backing-store size, no document overflow, non-collapsed editor geometry, and Orbifold's estimated rendered text boxes for overlap or invalid layout.
The smoke check launches headless Chrome with WebGPU enabled and fails on
browser exceptions, console errors, failed asset loads, missing WebGPU, a
runtime that does not reach Orbifold's first-frame readiness signal, or browser
actions/keyboard shortcuts/piano-roll pointer and wheel gestures that cannot
create, drag, resize, scroll, zoom, persist clip notes, and resize the main
workspace panels. It clicks ordinary toolbar buttons through the canvas hit
testing path. It also exercises browser save/open file flows plus Scala,
Lumatone key-map, and WAV asset imports through real browser file inputs, then
uses the imported browser WAV as the project sample instrument. It verifies
dirty-open confirmation before the browser picker appears and browser title
updates for dirty and loaded project states. It verifies
Web MIDI refresh/connect, note-on, note-off, note velocity, and recording
through a deterministic browser MIDI stub. Web Audio coverage verifies browser
output discovery, sink routing/default-sink fallback evidence, context creation,
processor attachment, resume request, Orbifold's connected audio state, and
nonzero samples from the browser A4 test-tone path.
The same smoke run reloads the browser page and verifies that the saved project
session, browser-loaded Scala/key-map resources, browser-imported sample
instrument, browser-imported asset, persisted panel-visibility settings, and the
web UI-scale reload path restore from browser storage. It also resizes Chrome to
a high-DPI viewport to catch canvas scaling regressions.
The visual capture script writes compact, desktop, high-DPI, and 4K browser
visual artifacts plus a manifest under screenshots/web/ for manual
inspection. When headless Chrome returns transparent WebGPU screenshots, it
falls back to an SVG paint snapshot exported by the live wasm runtime and
records the browser screenshot attempts in the manifest.
The Pages workflow captures and uploads the same visual artifact set for both
the local build artifact and the deployed Pages URL.
Use docs/web_parity_audit.md before claiming browser parity; it separates the
automated evidence above from manual checks that require a real browser,
deployed Pages site, audio output, and Web MIDI hardware. The manual-device
script has a --preflight mode that checks Chrome, Node WebSocket support,
secure-context eligibility for Web MIDI, and the deployed artifact fingerprint
without opening the interactive session. The
full manual run opens a real Chrome session, prompts for audible Web Audio with
sink-routing evidence, hardware Web MIDI press/hold plus release evidence,
audible hardware-MIDI monitoring, hardware MIDI recording press/hold plus
release evidence, audible recorded-note monitoring, real file-picker flows,
checkpointed shortcut parity for transport/editing/file/help/UI zoom, and
requires browser keydown evidence for every shortcut checkpoint. It also prompts
for checkpointed piano-roll parity for note, velocity, scroll/zoom, seek/loop,
and panel-resize workflows with browser pointer/wheel evidence, records initial
plus resized/high-DPI visual evidence plus browser reload persistence evidence
and the deployed artifact fingerprint, and writes a JSON report under
reports/. Validate that report with
./scripts/check-web-manual-report.mjs reports/ before treating the manual
device pass as release evidence. Add --finalize to the manual-device command
when you want a successful manual pass to immediately run the manual-report
validator, final Pages parity gate, and saved-evidence completion verifier.
Use ./scripts/check-web-parity-status.mjs reports/ --url https://<user>.github.io/<repo>/
as a quick diagnostic when you need to know which saved evidence artifact is
still missing for a specific deployed target; it does not replace the final
parity gate or completion verifier.
After the manual report exists, ./scripts/check-web-parity-gate.mjs runs the
deployed live/layout/smoke checks, captures deployed visuals, validates the
manual report, rejects stale reports whose artifact fingerprint no longer
matches the live Pages site, and writes a final gate report under reports/.
Use ./scripts/check-web-parity-complete.mjs reports/ --url https://<user>.github.io/<repo>/
as the final saved evidence check; it requires a passing gate report for that
target, a validated manual-device report, a saved visual capture manifest with
all required viewport artifacts, non-skipped visual capture, and matching
manual/live artifact fingerprints. It also rejects stale completion evidence
when the manual report is newer than the gate or when the visual manifest is not
under the gate's visualOut path and capture-step time window.
For a tester-oriented walkthrough of first launch, device setup, tuning, note
editing, saving, recovery, display sizing, and troubleshooting, see
docs/first_run.md. For deeper audio, MIDI, settings, project, and autosave
diagnostics, see docs/troubleshooting.md.
- Launch the app with
cargo run. - If the right panel opens to
DEVICESwithSETUP REQUIRED, use the MIDI INPUTS and AUDIO OUTPUTS sections to refresh, select, and connect devices. - Click
Controlafter setup to return to synth, tuning, and recording controls. - Use
Ch Allin the control panel to cycle a MIDI channel filter when you want Orbifold to respond to only one incoming channel. The selected filter is saved with the rest of the app settings. - Use
Mutein the control panel when you need to silence output without changing the saved master gain. - Load a Scala scale and Lumatone
.ltnpreset, or use the current saved defaults. Seedocs/lumatone_setup.mdfor the current scale/key-map workflow and limitations, anddocs/lumatone_troubleshooting.mdfor manual validation and wrong-note diagnosis. - Toggle
Metronomein the right control panel if you want a click while recording. - Toggle
Rec quantizein the clip panel to choose whether new recordings snap to the active grid. - Use the transport mode button to choose
ReplaceorOverdub, then pressRecord, play a phrase, and pressStop RecorStopto finish recording. - Use the quantize and piano-roll controls to tighten timing and edit notes.
- Press
Playto loop the clip. - Use
Save,Save As,Open,Open Recent, or a visible recent-project row for project files.
The workspace layout can be adjusted while working. Drag the vertical splitters
between the browser, clip panel, editor, and right panel to rebalance
horizontal space; drag the splitter above the piano roll to give the editor more
or less height. When both Assets and Scales are visible in the left browser,
their divider can also be dragged. Use the right-panel layout reset action to
return to default proportions.
The right panel can switch between Control, Devices/Setup, and Settings. Use Settings for UI zoom, browser/clip visibility, layout reset, and saving preferences.
Keyboard shortcuts include the core editing and transport paths below. See
docs/keyboard_shortcuts.md for the fuller reference.
?shows a compact shortcut reference in the status bar.TabandShift+Tabmove focus through visible controls;Enteractivates the focused control.Spacetoggles playback.Rtoggles recording.Mtoggles the metronome.Qquantizes the selected note, or the whole clip when no note is selected.Shift+Qtoggles record quantize.Gtoggles piano-roll snap off and back to the previous grid value.Pruns All Off as a panic/reset action.Nadds a note at the playhead.Dduplicates the selected note.Homereturns the playhead to the loop start.- Arrow keys move or transpose the selected note.
Shiftplus left/right resizes the selected note.Shiftplus up/down adjusts selected-note velocity.DeleteorBackspacedeletes the selected note.Ctrl/Cmd+Ccopies the selected note.Ctrl/Cmd+Vpastes the copied note at the playhead.Ctrl/Cmd+Nstarts a new project; unsaved changes require a second confirmation.Ctrl/Cmd+Ssaves.Ctrl/Cmd+Shift+Ssaves as a new project path.Ctrl/Cmd+Oopens; unsaved changes require a second confirmation.Ctrl/Cmd+ZandCtrl/Cmd+Yundo and redo.Ctrl/Cmdplus+,-, or0adjusts or resets UI zoom.Esccancels a pending discard confirmation, or clears the selected note when no confirmation is pending.
The right control panel also has visible zoom controls for the same UI scale setting.
Closing the window with unsaved project changes requires a second close request, so an accidental window close does not immediately discard work.
Reusable sound content belongs in audio_assets/:
audio_assets/samples/for one-shots, loops, recordings, and imported clipsaudio_assets/instruments/for instrument definitions, multisample maps, and sample setsaudio_assets/presets/for synth, effect, routing, and project sound presetsaudio_assets/impulses/for impulse responses and convolution assets
The left browser scans these folders on startup. Use Refresh after manually adding files, or Import to copy a file into the selected asset category.
The browser is currently a library surface with WAV sample preview and project
sample-instrument assignment. Full sampler/instrument management is still part
of the usability backlog. See docs/asset_browser.md for supported file types,
preview limits, import behavior, conflict renaming, and current limitations. See
docs/asset_to_sound.md for the current built-in-synth sound path and asset
workflow limits.
cargo fmt
cargo check
cargo test
cargo clippy --all-targets -- -D warningsRun the app with:
cargo runBuild the current browser app with:
./scripts/build-web.sh distThe web build compiles the shared Orbifold app state and action dispatch into
wasm through Operad's web runtime. Browser project open/save uses the same
.orbifold text format through browser file APIs, and Scala .scl plus
Lumatone .ltn loading use the same parsers as desktop. Browser MIDI input uses
Web MIDI in supported browsers and feeds the same MIDI handling path as desktop.
Browser audio uses Web Audio and the shared synth engine. Browser settings reuse
the same plain-text settings format in localStorage, and the browser keeps the
latest project session in localStorage so reloads can restore the current
.orbifold state. The browser surface uses the shared Operad document builder,
including piano-roll note editing, panel splitters, loop-end drags, viewport
thumb drags, timeline ruler seeking, and piano-roll wheel navigation. Browser
asset import can load WAV samples for preview and sample-instrument use, persists
browser-imported asset bytes in IndexedDB with a legacy localStorage
migration/merge/fallback, and restores them before the saved browser project
session so sample instruments survive reloads within browser storage quota.
Browser keyboard shortcuts route
file, scale, key-map, asset, and MIDI actions through the same browser APIs as
pointer clicks, and dirty project confirmation is preserved before replacing
the current browser project. Browser text edits for BPM, root, base frequency,
scale search, and asset search use the same shared command handling as native.
The browser tab title uses the same project name and dirty-state formatting as
the native window title. Web UI-scale actions persist the new setting and reload
the page so the fixed Operad web runtime scale is reapplied on startup.
web/index.html keeps a static DOM fallback for browsers where WebGPU cannot
start or does not report startup completion promptly. The generated site lives
under dist/ and is deployed by .github/workflows/pages.yml.
Developer notes for the current plain-text settings, project, backup, and
autosave files live in docs/file_formats.md.
The current codebase architecture overview lives in docs/architecture.md.
The audio/MIDI threading model lives in docs/audio_midi_threading.md.
The Operad integration model lives in docs/operad_integration.md.
The UI testing workflow lives in docs/ui_testing_workflow.md.
The web parity audit lives in docs/web_parity_audit.md.
Guides for adding UI controls/actions and project commands live in
docs/add_ui_control.md and docs/add_project_command.md.
The current prototype limitation list lives in docs/known_limitations.md.
Linux desktop metadata lives at packaging/linux/orbifold.desktop. It is a
minimal launcher template for packaged installs and expects the installed binary
and icon to both be named orbifold. A matching scalable icon lives under
packaging/linux/icons/hicolor/scalable/apps/, the 64px PNG app icon lives under
packaging/linux/icons/hicolor/64x64/apps/, and the browser-style favicon is
favicon.ico.
The GitHub Pages shell lives in web/index.html. The wasm entry point is
examples/orbifold_web.rs, and scripts/build-web.sh copies the generated
pkg/ output plus the favicon assets into dist/. The build also writes
dist/.nojekyll, and scripts/check-web-dist.mjs verifies the Pages artifact
contains the wasm loader, wasm binary, icons, relative asset references, and
runtime-ready/fallback hooks. scripts/check-web-layout.mjs verifies the live
canvas and editor geometry across compact, desktop, high-DPI, and 4K browser
viewports. After deployment, scripts/check-web-live.mjs performs the same
artifact-shape check against the published Pages URL.
Release notes and release checks live in CHANGELOG.md and
docs/release_checklist.md. The release workflow lives in
docs/release_workflow.md.
Use screenshot mode to capture the current Operad surface:
cargo run -- --screenshot
cargo run -- --screenshot-size=1200x760Screenshots are written to screenshots/ as timestamped PNGs, and
screenshots/latest.png is updated for quick inspection. Screenshot mode skips
audio and MIDI hardware probing so visual QA can run on machines without working
devices, avoids saving device settings from that no-probe startup, and may
intentionally show the right panel in Devices/Setup mode because setup is
incomplete. Use --screenshot-size=WIDTHxHEIGHT to capture a specific output
image size, such as the minimum supported layout or a 4K monitor check.
Visual review matters. Passing tests or producing a PNG does not prove the UI is
usable; inspect the image after layout changes. For broader checks, use
docs/manual_qa_checklist.md.
Useful development checks:
cargo fmt --check
cargo check
cargo test
cargo run -- --screenshot
cargo run -- --screenshot-size=3840x2160
./scripts/capture-web-visuals.mjs https://andrewp2.github.io/Orbifold/Then inspect screenshots/latest.png and the latest screenshots/web/ run.
Errors should be handled according to whether the app can still do useful work.
- Graphics/device initialization failures may still be unrecoverable.
- Missing audio or MIDI hardware should leave the UI usable with a visible status.
- Recoverable runtime failures should be shown in the UI status area.
- Parsers should reject malformed input instead of substituting hidden defaults.
- Dirty project edits are written to
orbifold_autosave.orbifoldas a basic recovery file. If that file exists on startup, the left session strip shows aRecoveraction that loads it as an unsaved project and aDismissaction that removes stale recovery files from a clean project.
The current usability gap inventory lives at
docs/orbifold_usability_gap_analysis.md.