CYBURDINE.com
--:-- ---
The Soniflux application on a dark blue field: a canvas of small processing nodes wired with green cables into a tall console node, a bar ruler and transport along the bottom, with the word SONIFLUX set large behind it and a short chain of outlined nodes drawn to the right.

Soniflux

PROJECT active build
typescriptweb audioreact flowpythondemucsace-stepself-hosted
In progress. Every screenshot on this page is the “Bless Your Heart” rebuild project, September 2026, running on the machine that holds the GPU. The take nodes are wired to reference stems as stand-ins until the real takes are recorded.
Soniflux is a digital audio workstation that runs in a browser tab, built around AI nodes instead of around a track list. I built it to replace the way I had been making music for the last year: record a take, upload it to one AI tool, download the result, upload that to another, download again, drag everything into a DAW, and start over the moment I wanted to change a line. The songs are for Cywren. She is a character I made up to write songs for. The first one came out better than I expected, so I kept the same voice prompts and kept writing, and each song got a little easier to produce than the last. At some point that turned into a proper project: give her a voice, motives, a personality, write those into the lyrics and the feel of every track, and build an album around her. Soniflux is the studio that album gets made in. The idea is simple to say. I sing and play; the graph makes it sound like the record. My dry takes go in on the left, pass through nodes that clean them, tune them, reshape them and, where I ask for it, re-sing them in her voice, then land on a console where I mix them the way I would mix anything else, and bounce.

Six months, by hand

The first three Cywren songs took most of six months. The lyrics and the basic melodies were in my head from the start; the time went into the pipeline. Every song I changed some of the tools, updated the scripts that strung them together, generated parts, and then assembled the parts in a DAW by hand. Almost every song was built through that workflow, and none of it carried over cleanly to the next one.
6 months, 3 songs across a different set of tools each time. The melody was never the slow part.
What changed was a conversation. I pointed Claude Fable at the scripts and the process, spent about thirty minutes describing what I actually did to a song and where it hurt, and then asked for one tool that could do all of it in the browser: assemble my recordings, pass them through the AI steps, and hand the result back to a console where I could polish it. Everything below came out of that request over the following days, in milestones I reviewed one at a time. The part I care most about is the middle. If I do not like a sound or want to change a lyric, I do not go back to the start. I chain a different set of nodes onto the take and run it again.

The graph is the session

There is no track list. A song is a graph: source nodes on the left, a console in the middle, buses and a master chain on the right, and every wire is a signal path you can see. The graph is the only thing the project file stores, and the audio engine is compiled from it. That one decision is what lets the same graph drive live playback in the tab and a deterministic offline bounce on the server, because there is exactly one description of the mix and two things that read it.
The whole Bless Your Heart rebuild graph: five take chains stacked on the left, each a row of small effect nodes, a tall console node in the centre with eighteen inputs, drum bus and reverb and delay returns above and below it, and a master chain running right to a bounce node. A row of section notes runs along the bottom.
// the rebuild project. 59 nodes, 66 wires, one song
Thirty-seven node kinds exist today, and every one of them has a working DSP implementation behind it; a kind added to the schema without one is reported as an error rather than passing audio through silently. They fall into sources (a file, a tone, a step-sequenced drum kit), filters and EQ, dynamics, colour, time and pitch, the AI nodes, the console and its buses, sends and VCAs, and outputs. Double-click any node and a device panel opens over the canvas with its full controls.

From a take to her voice

This is the loop the whole tool exists for. One vocal take, start to finish:
  1. Record dry Close mic, no reverb, no tuning. The take goes into a File Source node. Bass is a DI; synth is a line out. The amp, the width and the sidechain pump all live in the graph.
  2. Clean it A Stem Split node runs Demucs on the take and keeps only the vocal, so room noise, bleed and the click track are gone before anything else touches it.
  3. Put it in tune and on the grid Tune analyses the pitch, snaps each note to the key or to a harmony above it, and writes the correction as an automation lane on a Transpose node. Quantize does the same for timing against the tempo map. Both are curves you can edit by hand afterwards.
  4. Re-sing it Prompt Regenerate is audio-to-audio: my take goes in, a prompt describes her, and a strength control decides how much of the input survives. Low strength keeps my phrasing and inflection and changes the timbre; high strength keeps only the shape. Generate on the ACE-Step engine takes lyrics with verse and chorus markers and sings them outright, which is how a line I have only hummed gets words.
  5. Iterate Do not like the second line? Change the prompt, or sing it again with the inflection I want, and run that one node. Nothing downstream has to be rebuilt, because downstream is just wires.
  6. Bend it High-pass, gate, de-ess, compress, EQ, a little tape. The same chain a mix engineer would build, as nodes.
  7. Mix and bounce Into a console strip, onto a bus, through the master chain, out to a file rendered on the server.
An animated walk through the Soniflux interface: the full graph, then a zoom to the lead vocal take with its waveform, the take's device panel, the Demucs stem split node with its Run button and GPU readout, the vocal dynamics chain and the compressor and EQ panels, one backing vocal take fanning out into a double and two harmonies, the AI generate panel with its prompt and lyrics fields, the eighteen-strip console, the master chain with meters moving during playback, the export dialog, and back to the full graph.
// the walkthrough. take in, AI in the middle, console on the way out
The lead vocal File Source node zoomed in, its waveform visible, with its device panel open on the right: the audio file with choose and remove buttons, gain, start offset, trim start and end, loop, and a signal section listing one stereo output.
// a take. a file, a gain, and where on the timeline it sits
The Stem Split node beside the high-pass that follows it, with its panel mid-run: a Running label with a Cancel button, a progress bar reading Loading htdemucs_ft, the line NVIDIA GeForce RTX 3090 with 5.6 GB free, the model selector and the job id.
// clean the take. the job runs on the GPU box, the tab just watches
One backing vocal take feeding a Demucs clean node, whose single output fans out to three nodes: a chorus double, a harmony transposed up three semitones and a harmony transposed down four.
// one backing-vocal pass becomes a double and two harmonies
The backing vocals are the clearest example of what a graph buys you. I sing the chorus hook once, in unison. One node cleans it, and its single output feeds three: a three-voice chorus for the double, a transpose up a third for the high harmony and a transpose down a fourth for the low one. Three parts from one take, and if the take changes, all three follow.

The AI nodes, and the GPU they share

Four kinds do the AI work, plus two analyses that decide how far to move a take rather than generating anything.
Stem Split
Demucs, four models to pick from. Each stem is its own output port, so you can wire only the vocals on and leave the rest unconnected.
Generate
Text to audio. Stable Audio Open by default, ACE-Step when there are lyrics to sing. Pieces longer than a model can make are built from overlapping passes with one seed per pass, so a seeded piece is the same piece every time.
Prompt Regenerate
Audio to audio. The incoming stem plus a prompt, with strength from 0 (keep the input) to 1 (ignore it).
Analyze
Tempo and key from the audio. It seeds the tempo map; the key it reports on a whole mix is known to be off by a fourth, so the bass gets a vote.
Tune
Pitch correction as a decision, not an effect: where each note sits and how far to move it, handed back as an automation curve on a Transpose.
Quantize
The same for timing, against the tempo map.
The Generate node's panel: a prompt asking for driving pop-rock rhythm guitar, a negative prompt excluding drums and vocals, an empty lyrics field, an engine selector, steps, guidance scale, a forty-five second duration, and clip placement controls.
// generate. the guitar bed if I don't play one; lyrics go in the box when she sings
Every one of these runs on the server, on an RTX 3090 that is not in the same room as the laptop. The tab submits a job, follows its progress over a stream, and pulls the result into the graph as an asset when it lands, so a closed tab does not lose the work. Two things had to be true for that to be livable. The first is that the GPU is leased, not grabbed. A broker hands out time on the card with a deadline, and a job that arrives while another is running waits in a queue with a position it can report, rather than being refused or, worse, being admitted and crashing the job already there when the VRAM runs out. A model's weights are fetched before the lease is taken, so the download never counts against it, and a driver that overruns its deadline is stopped, not asked nicely. The second is that the heavy models do not run inside the service. ACE-Step pins versions of its dependencies the service cannot share, so it runs as a subprocess in its own environment, with a contract that is one progress line per stage and one file at the end. The first generation on the box ran to completion and then failed on the very last line, saving the file; the fix was a shim, and the lesson was that the contract has to include the save.

Bending each track

After the AI has had its say, the rest is ordinary mixing, and the nodes are the ones a mixer would reach for. The lead vocal chain in the rebuild is a high-pass at 80 Hz, a gate, a de-esser at 6.5 kHz, a 4:1 compressor, a four-band EQ and four decibels of tape at forty percent mix. Every parameter can be automated, and automation is scheduled onto the audio parameters themselves rather than driven by a timer, which is why it works identically in the bounce.
Two take chains side by side: the lead vocal running through Demucs, high-pass, gate, de-esser, compressor, EQ and tape saturation, and beneath it the backing vocal fanning into its double and harmonies, with the bass chain starting below.
// the vocal chains. each node shows its two most important numbers on the card
The vocal compressor's panel: threshold, ratio, knee, attack, release, RMS detection, lookahead, makeup, auto makeup, an external sidechain switch and a sidechain high-pass, with a Freeze section above the parameters.
// a compressor. soft knee, lookahead, a key input on the card
Three more chains: the bass DI through high-pass, gate, compressor, a tanh amp stage, EQ and a kick-keyed ducker; the synth through EQ, chorus, width and its own ducker; and the guitar DI through an asymmetric amp, a cabinet filter, EQ, and a left gain and a seventeen millisecond right delay that fake a double.
// bass, synth, guitar. the amp is a node; the double is a delay
Two controls on every node matter more than the rest. Bypass is an edit to the graph, not a flag inside the node: a bypassed node is left out of the compiled plan and its input is wired straight to wherever its output went, so it costs nothing and, importantly, contributes no latency that the rest of the mix then has to be delayed to match. Freeze renders what a node puts out, including everything upstream that feeds only it, stores the result as an asset and swaps the node for a player of it. It exists because the live graph runs on one audio thread, and this song’s five oversampled saturators and two phase-vocoder transposes need about 122% of a core in real time. A mix you cannot audition is not a mix. Freeze all does it for every chain at once and leaves the console live.

The console

The console is one node with a real desk inside it: up to sixty-four strips, four buses, four sends, eight VCAs and a master section. It is built to the way a console behaves rather than the way a plugin chain does, and the differences are the point.
  • Every strip has trim, a switchable high-pass, EQ and dynamics with pre and post ordering, four sends each with pre or post, bus and VCA assignment, mute group, meter tap, pan, fader, automation arm, and mute, solo and solo-safe
  • Solo has three modes. PFL and AFL replace what the monitor output carries and leave the main mix alone, so soloing a channel cannot ruin a take being recorded; only solo-in-place mutes the others on main
  • Monitor dim and mono live downstream of main. The bounce cannot hear the control room, and there is a test that says so
  • VCAs scale the fader rather than replacing it, so post-fader sends follow the VCA and pre-fader sends do not
  • Buses and sends are output ports, not dropdowns: a wire you can see beats a target that dangles when the node it named is deleted
The console panel: eighteen channel strips labelled Lead Vox, BV Double, BV Harmony, Bass, Synth, Guitar L and R, Drums and more, each with trim, HPF, EQ and dynamics buttons, four send rows, bus, VCA and mute group selectors, pan and a long fader, with a master section on the right showing pan law, solo mode, master fader and monitor dim and mono, and an automation lane along the bottom.
// the console. eighteen strips for this song, sixty-four if you need them

The bounce

Export renders the graph through the same DSP units you hear, then posts the result to the server for encoding. By default the render itself runs on the server too, because that is the machine the audio already lives on: only the graph crosses the network, and the bounce survives a closed tab. The browser is the fallback.
  • The graph is recompiled at the export sample rate, because delay compensation is solved in samples and a plan built for 48 kHz would misalign every compensated edge at 96
  • The head of the file is trimmed by the latency arriving at the output, so two bounces of the same music null against each other instead of differing by however long the plugin chain happened to be
  • The engine always hands over 32-bit float. Bit depth, dither and loudness normalisation are applied once, by ffmpeg, on the server
  • A graph can hold several Export nodes and each renders only its own upstream subtree, which is how stems come out
  • Every finished bounce is listed in the dialog as a download, because the server is usually not the machine the browser is on
The master chain during playback: 2-Bus, master EQ, a gentle multiband, two decibels of tape, a true-peak limiter at minus one, a LUFS meter and Master Out, each node with a small green level meter lit, a bounce node hanging off the limiter, and the transport reading ten seconds in at 150 BPM.
// the master chain, rolling. every node carries its own meter
The Export dialog: which output to bounce, a file name, format, bit depth, sample rate, MP3 bitrate, dither, loudness target and true-peak ceiling, and a list of four earlier bounces with their sizes and download links.
// export. rendered through the nodes you hear, encoded once on the server
A freeze, a stretch and a bounce are all the same shape of work, so they share one queue that the whole app can see. The node a render belongs to draws its progress along its bottom edge, the Renders panel lists everything in the order it will run with a way to reorder or cancel, and cancelling reaches into the render rather than throwing the result away minutes later. The saturator’s oversampler was the first thing profiled in the repo: it was 87% of a channel’s render cost, and skipping the taps that are zero by construction took a bounce of it from 6.5 seconds to about 2, with a test that holds the new code sample-for-sample to the old.

Two details that earn their keep

The Settings dialog with a single control, Autosave, set to every fifteen minutes, and a paragraph explaining that the save waits for the graph to go still and that the preference lives in this browser rather than in the project.
// autosave. a ceiling on what a crash can cost, not a substitute for deciding a version is worth keeping
Autosave waits three seconds for the graph to go still, so it never lands in the middle of a fader drag, and gives up waiting after thirty, because an automation pass is a continuous edit and would otherwise hold a whole take unsaved. The cadence is a preference of the browser, not a property of the song, so it never reaches the project file or the server. Nothing in Soniflux deletes anything. Removing a project, a stem or a bounce renames it into a trash folder under a timestamped name the app reports back, so recovery is a move in a shell and no timer ever throws audio away on its own.

Under the hood

A pnpm workspace with three packages, two apps and one Python service. The engine has no idea whether it is running in a tab or in a headless render on the server; both hand it a context and read back a plan.
packages/schema
Every node kind, its parameters and the project file, as validated types.
packages/engine
The graph compiler, delay compensation, transport, tempo map, automation, freeze and the offline renderer.
packages/dsp
The worklet layer: compressor, limiter, saturation with oversampling, reverb, pitch and time.
apps/web
The canvas, the device panels, the console, the timeline. React and React Flow.
apps/server
Projects, content-addressed assets, the render queue, ffmpeg encoding, downloads.
services/ai
FastAPI: separation, generation, analysis and tuning, the GPU lease broker, and the ACE-Step driver in its own environment.
It deploys with one script to the machine that has the GPU and the storage, behind nginx with TLS on both its LAN name and its tailnet name, under systemd. Assets are stored under the hash of their contents and hard-linked rather than copied, so re-importing a 400 MB multitrack costs no disk. A licence gate runs in the build and refuses model weights whose terms the project has not accepted.

Why it looks like this

A DAW hides the signal path behind a track list. That is fine when the signal path is a microphone and a fader. It is not fine when half the path is a model that re-sings the take, because then the interesting decisions are exactly the ones the track list hides. Soniflux draws the path. A take is a node, her voice is a node, the console is a node, and the wire between them is the whole explanation of how a song got from what I sang to what she sings. When something is wrong, I can point at it. When something is right, I can copy it to the next song. That is what six months by hand never gave me, and it is the only reason this exists.
Terminal Frame