Futureboard Studio is a WebUI-based DAW built to start in the browser, scale into Electron, and eventually share its workflow with a native C++/Skia runtime in future.
The goal is simple:
Build a serious DAW that feels like a desktop editor, runs with a WebUI workflow, and can use native audio when the browser is no longer enough.
Futureboard is not meant to be a toy browser DAW. It is designed around client-side processing, native desktop integration, folder-based projects, low-latency playback, extensible plugins, and a scalable render architecture.
Futureboard Studio is in active development.
Working / in progress:
- React / Vite WebUI
- Electron desktop client
miko://app/index.htmllocal ASAR loading- Folder-based project workflow
- Native Rust audio addon loading
- Native metronome output
- Electron file browser with drive roots
- Project indexing
- Waveform rendering and peak cache system
- Track arrangement UI
- Mixer / inserts / effect editor UI
- Built-in plugin package structure
- Initial plugin manifest direction
- DAUx native audio backend direction
Known unstable areas:
- Native arrangement track playback
- Auto Import to Project
- Native snapshot media path resolution
- Pitch / speed processing
- Chunked waveform peak rendering for very long files
- Reverb / delay realtime parameter update path
- VST3 plugin host (in plan)
- Full audio graph / routing / bus / return handling
Web Version
= React / Vite
= WASM audio processing
= browser-safe storage/cache
= lightweight projects and cloud entry point
Electron Client
= WebUI desktop shell
= local folder projects
= native file browser
= DAUx native audio engine
= native addons
= low-latency desktop workflow
Native Future (in Q2 2027)
= C++ / Skia / SphereEngine
= V8 + Yoga UI runtime
= native plugin/editor architecture
= same DAW concepts, deeper native performanceCurrent audio rule:
Electron = DAUx: OS Audio Backend
Web = WASM onlyThis avoids confusing states where Electron looks native but silently routes playback through old Web/WASM paths.
apps/
├─ web/
│ └─ React/Vite WebUI
│
├─ electron/
│ └─ Electron desktop client
│
├─ native/
│ ├─ include/
│ ├─ src/
│ └─ CMakeLists.txt
│
crates/
├─ SphereDirectAudioEngine/
│ └─ Rust Audio Engine for Electron
│
└─ SphereWebAudioCore/
└─ Rust/WASM web audio core
plugins/
└─ Equz8/
├─ Core/
├─ Editor/
└─ package.jsonPackaged Electron loads the bundled WebUI through a custom protocol:
miko://app/index.htmlThe WebUI is packed into app.asar.
Expected flow:
Electron packaged app
→ register miko:// protocol
→ serve dist/index.html, JS, CSS, WASM, fonts, images
→ load miko://app/index.htmlThe hosted Web version remains separate.
Web version = hosted web app
Electron client = bundled local ASAR appNative addons:
DAUx.node
= audio device
= transport
= mixer
= routing
= realtime DSP graph
= metronome
= native project playback
DAUxPluginHost.node (in plan)
= VST3 / plugin scanning
= plugin loading
= parameter bridge
= plugin processing
= native editor bridge laterThe current native audio issue is not the output callback. Metronome already proves native output works.
The critical path is:
Project clips
→ asset manifest
→ resolved mediaPath
→ native snapshot
→ Rust runtime clips
→ arrangement playbackDAUx is the planned native low-latency backend layer.
Target backends:
Windows
- WASAPI
- MME fallback only
macOS
- CoreAudio
Linux
- ALSAFuture optional backends:
ASIO
JACK
PipeWireDAUx rules:
- no allocations in audio callback
- no locks in audio callback
- no filesystem access in audio callback
- no JSON parsing in audio callback
- no plugin scanning/loading in audio callback
- use lock-free command queues
- use immutable graph snapshots
- expose backend/device/buffer/latency/glitch status
Electron desktop mode uses folder-based projects.
<Project Name>/
├─ Cache/
│ ├─ Peaks/
│ ├─ Waveform/
│ ├─ Processed/
│ └─ Analysis/
│
├─ Media/
│ ├─ Audio/
│ ├─ MIDI/
│ ├─ Samples/
│ └─ Imports/
│
├─ Rendered/
│ ├─ Mixdowns/
│ ├─ Stems/
│ └─ Bounces/
│
└─ <Project Name>.mochiprojRules:
Media/is persistent.Cache/is regeneratable.Rendered/is user output..mochiprojstores project metadata and relative paths.- Audio files must not be embedded into
.mochiproj. - Runtime
File,Blob,AudioBuffer, or decoded buffers must not be the source of truth.
In Electron folder-project mode, any audio file added to the arrangement must be imported into the project package first.
Correct flow:
Drop / Import Audio
→ copy into <Project>/Media/Audio/
→ create asset manifest entry
→ create clip with assetId
→ generate/request waveform peak cache
→ save/update .mochiproj
→ send native snapshot with mediaPath
→ SphereAudio schedules clipNo Electron audio clip should depend only on a temporary runtime file reference.
Native Rust playback requires each audio clip to resolve to a real filesystem path.
Expected native clip snapshot:
{
id: string;
trackId: string;
assetId: string;
mediaPath: string;
startBeat: number;
durationBeats: number;
offsetSeconds?: number;
gain: number;
mute?: boolean;
}Bad:
mediaPath: null
mediaPath: ""
mediaPath: miko://...
mediaPath: blob:...Good:
H:/Projects/My Song/Media/Audio/vocal.wavIf SphereAudio logs this:
1 clips (0 with paths)
all clips have null/empty mediaPath
RuntimeProject built 0 runtime clips
StartTransport: 0 clips scheduledthen Rust Audio is not the problem. The snapshot/media resolver is broken.
Current project schema may use:
files[]while the native snapshot expects:
assets[]
asset.relativePath
clip.assetIdIf clips currently have:
clip.fileIdthen native snapshot generation must support this mapping:
clip.fileId
→ project.files[]
→ file.relativePath / path
→ projectRoot + relative path
→ clip.mediaPathRecommended fix:
normalize files[] into assets[]
OR
make native snapshot resolver support both assets[] and files[]Resolver priority:
clip.assetId ?? clip.fileId ?? clip.sourceId ?? clip.importIdSearch order:
project.assets first
project.files secondElectron File Browser supports native filesystem browsing.
Target browser tree:
Browser Tree
├─ Quick Access
├─ Futureboard Studio
│ ├─ Loops
│ ├─ Presets
│ ├─ Samples
│ └─ Templates
├─ Drives
│ ├─ C:
│ ├─ D:
│ └─ E:
├─ Current Project
│ ├─ Media
│ ├─ Cache
│ └─ Rendered
└─ ImportsFile Browser requirements:
- compact DAW/editor panel
- drive roots in Electron
- browser-safe behavior in Web
- audio metadata badges
- cached/imported/missing states
- drag/import uses Auto Import to Project
- indexing status uses spinner/loading UX, not noisy numeric counters
- detailed indexing counts belong in Developer/Debug only
Waveform rendering must not be whole-file based.
Long files such as 2–3 hour recordings must use chunked progressive rendering.
Concept:
Waveform = tiled map rendering
visible chunks first
nearby chunks next
background chunks later
cache everything
draw only visible rangeElectron cache layout:
Cache/Peaks/<assetId>/
├─ meta.json
├─ ch0_spp8192_chunk000000.bin
├─ ch0_spp8192_chunk000001.bin
└─ ...Rules:
- no DOM waveform spam
- no full-file redraw
- no blocking UI during peak generation
- coarse chunks first
- finer chunks on zoom
- placeholder while missing
- cache is regeneratable
Pitch and speed processing is under active work.
Required shape:
clip.audioProcess = {
speedRatio,
pitchSemitones,
preservePitch,
mode,
quality,
};Rules:
- speed changes effective clip duration
- pitch with preserve pitch should not change visual duration
- cache key must include processing params
- processing must not reload the whole audio engine
- Web uses WASM
- Electron uses DAUx/native if implemented, or explicit WASM fallback
- no silent no-op
Expected visual behavior:
speedRatio 2.0 = half duration / half width
speedRatio 0.5 = double duration / double width
pitch +6 preservePitch = same widthFutureboard uses plugin package discovery through manifests.
Planned plugin root:
{appdir}/plugins/**/manifest.jsonExample:
{
"id": "futureboard.equz8",
"name": "Equz8",
"version": "0.1.0",
"type": "audio-effect",
"category": "eq",
"vendor": "Futureboard",
"entry": {
"core": "./Core/index.js",
"editor": "./Editor/index.html"
},
"params": "./Core/params.json",
"editor": {
"kind": "web",
"framework": "react",
"width": 720,
"height": 360
},
"capabilities": {
"hasEditor": true,
"supportsAutomation": true,
"supportsPresets": true
}
}Plugin architecture:
Core
= params
= schema
= DSP hooks
= preset model
= no React
Editor
= UI
= React / Vue / Svelte / Vanilla / Sphere UI
= communicates with host through protocol
Host
= owns params
= updates audio engine
= handles automationDo not hardcode built-in plugins directly into mixer/effect editor.
VST3 host will likely be native/Rust-based.
Temporary debug GUI can use egui.
Rust VST3 Host
├─ Plugin scanner
├─ Plugin loader
├─ Parameter inspector
├─ MIDI monitor
├─ Audio process test
└─ egui debug UIElectron integration:
Electron Main
→ sphere-pluginhost.node
→ VST3 host coreFutureboard UI must feel like a DAW/editor, not a web dashboard.
Style direction:
compact
dark
technical
editor-like
DAW-like
low-noise
high-density
subtle borders
cyan accentAudio settings should stay simple.
Current rule:
Electron = DAUx: OS Audio Backend
Web = WASM onlySettings > Audio should expose:
Electron:
Engine: DAUx
Backend:
- Windows: WASAPI, MME fallback
- macOS: CoreAudio
- Linux: ALSA
Buffer Size:
- 64
- 128
- 256
- 512
- 1024
Sample Rate:
- Device Default
- 44100
- 48000
- 96000Web:
Engine: WASM
Backend: browser-managed / unavailableDo not show confusing WebAudio/RustWASM/native hybrid options.
Useful search commands:
rg "WasmAudio|RustDsp|futureboard_core|WebAudioEngineAdapter|ClipScheduler|new AudioContext|AudioWorklet" apps packages frameworksrg "assetId|raw.assetId|function normalize|normalize.*Clip|fileId" apps/web/src/store/normalize.ts appsrg "mediaPath|buildNativeProjectSnapshot|loadProject|SphereNativeAudioEngineAdapter" apps packages frameworksImportant debugging logs:
[AudioEngine] active backend
[SphereAudio IPC] loadProject
[SphereAudio] RuntimeProject built
[SphereAudio callback] StartTransport
[NativeSnapshot] clip mediaPath existsSymptom:
Metronome works
Track playback silent
SphereAudio says clips have null mediaPathCause:
Project schema has files[]
Native snapshot expects assets[]
Clip has fileId
Resolver expects assetId / relativePathFix direction:
normalize files[] into assets[]
or native resolver supports files[]
resolve clip.fileId → project.files[] → relativePath → mediaPathElectron must copy imported audio into Media/Audio and create a project asset before creating timeline clips.
Processing path, cache key, visual duration, and native/Web backend routing need to be repaired.
Whole-file waveform generation breaks for multi-hour clips. Needs chunk/tile cache.
Example scripts may vary by workspace.
bun install
bun run build
bun run devElectron:
bun run --cwd apps/electron dev
bun run --cwd apps/electron buildWeb:
bun run --cwd apps/web dev
bun run --cwd apps/web build- WebUI is the primary UI workflow.
- Electron is the serious desktop runtime.
- Web version remains WASM-only.
- Electron uses DAUx/native audio.
- Folder projects are the source of truth.
- Media belongs in
Media/. - Cache belongs in
Cache/. - Runtime files/buffers are never project truth.
- Plugins are manifest-driven.
- Audio callback must be realtime-safe.
- No silent backend fallback.
- No fake native mode.
- If a backend is active, the UI must say so.
- If a feature is not wired, show honest status.
MIT License.
Futureboard Studio is intended to be open-source with optional donation/subscription/supporter layers for cloud, samples, stock plugins, AI, or additional services.