StemMixer
StableOne lane per stem of a song with Mute, Solo, a named level slider and a box to pick it for export, plus the separation state in words.
Also known as mixer, stems, audio mixer
Install with npm install @tattva/ui (setup), or copy the code from the kit in Downloads.
Mix and pick stems to export
Harbour Light stems
Split by AI ·4 stems
Vocals
Playing
Drums
Playing
Bass
Playing
Other
Muted
<StemMixer title="Harbour Light stems" status="ready" duration={124} position={34}
stems={stems} onLevelChange={setLevel} onMuteChange={setMuted}
onSoloChange={setSoloed} onExportChange={setExported}
exportNote="WAV · 3 downloads left this month" onExport={exportStems} />
const stems = [
{ id: "vox", name: "Vocals", peaks, level: 80, muted: false, soloed: false, exported: true },
{ id: "drums", name: "Drums", peaks, level: 70, muted: false, soloed: false, exported: true },
{ id: "bass", name: "Bass", peaks, level: 65, muted: false, soloed: false, exported: false },
{ id: "other", name: "Other", peaks, level: 55, muted: true, soloed: false, exported: false },
];Four stems, Other muted. Solo one to hear it alone; the rest say they are silent. Tick stems to change what Export takes.
When to use it
Lets a person hear and export the separate parts of a generated song.
Made of: Title and separation state · Stem rows (export box, name and what it is doing, Mute, Solo, waveform, level) · Export note and Export button
Use it for
- The stems step of an export Sheet in a music studio.
- Checking a vocal or drum part on its own before export.
- Telling a person plainly that stems are still being made, failed, or are not on their plan.
Not for
- The structure of a song in time: use SectionLane.
- Playing one finished clip: use AudioPlayer.
- Choosing a file format and licence: use ExportSheet.
Do and avoid
Harbour Light stems
Split by AI ·3 stems
Vocals
Playing
Drums
Playing
Other
Muted
Do
- Name every stem plainly: Vocals, Drums, Bass, Other.
- Say how many stems Export will take, and any download limits beside it.
- Say whether a failed split was charged.
Avoid
- Do not show a percentage for a split that is really queued.
- Do not colour muted or soloed rows only; the words say it.
- Do not use amber for separating or plan-locked. Neither needs the person to act.
On a phone
- Rows stack: the export box and name, then Mute and Solo, then the level slider.
- Waveforms are hidden when the mixer is narrower than 24rem (about 400px).
- Mute, Solo, sliders and buttons grow to 44px on touch screens.
Seen in products
- Suno offers Auto Split into up to 12 stem categories, Split from Mix, and Advanced Split on its top plan only (June 2026).
- Suno Studio exports stems as audio or MIDI.
Properties
| Prop | Type | Default | Description |
|---|---|---|---|
| stemsrequired | Stem[] | none | Stems: id, name, peaks, level (0 to 100), muted, soloed, exported. |
| statusrequired | "separating" | "ready" | "failed" | "plan-locked" | none | Where the split is. |
| duration | number | 0 | Song length in seconds, for the waveforms. |
| position | number | 0 | Playhead in seconds, drawn on each waveform. |
| onLevelChange | (id: string, level: number) => void | none | Level slider moved. |
| onMuteChange | (id: string, muted: boolean) => void | none | Mute pressed. |
| onSoloChange | (id: string, soloed: boolean) => void | none | Solo pressed. Several stems can be soloed. |
| onExportChange | (id: string, exported: boolean) => void | none | Export box ticked or cleared. |
| onExport | (ids: string[]) => void | none | Exports the picked stems. Leave out to hide Export. |
| exportNote | string | none | Format and limits beside Export. |
| separatingNote | string | "Splitting the song into stems. About a minute." | What is happening while separating. No percentages. |
| failedNote | string | "The song could not be split. You were not charged." | What happened and whether it was charged. |
| onRetry | () => void | none | Shows Try again when failed. |
| planNote | string | "Stems come with the Pro plan." | Which plan has stems. |
| onSeePlans | () => void | none | Shows See plans when plan-locked. |
| placeholderCount | number | 4 | Placeholder rows while separating. |
| title | string | "Stems" | Heading. |
| headingLevel | 2 | 3 | 4 | 5 | 6 | 3 | Level of the heading. |
| className | string | none | Extra classes. |
"Show code under the preview gives the code for the example on the stage."
In Figma
In Figma, one StemMixer switches all of this from its properties panel, across 4 variants. Each property sets the React prop beside it, so design and code use the same names.
| Figma property | React | Values |
|---|---|---|
| Status | status | Separating, Ready, Failed, Plan-locked |
| Title | title | Any text |
| ExportNote | exportNote | Any text |
Tokens
Change one of these and every part that uses it follows.
--surface--surface-sunken--border--border-strong--fg--fg-muted--lime--accent--radius-card--radius-control--dur-fast
Built in
- Mute and Solo are toggle buttons with aria-pressed, named with the stem: "Mute Drums", "Solo Vocals".
- Each level is a native range slider named "Drums level", read as a percentage.
- Each export box is named "Export Drums". Export says how many stems it will take and turns off when none are picked.
- Each row says in words whether the stem is playing, muted, soloed or silent while another is soloed.
- Waveforms are pictures only and hidden from screen readers.
- One polite status line says mute, solo and separation changes once each; the mixer is aria-busy while separating.
- The separating dot only pulses with motion allowed; otherwise it is still next to the word Separating.
What you need to do
- Apply mute, solo and level to the audio you play; the mixer only reports them.
- Pass a separatingNote with a real time or queue position, never a made-up percentage.
- Say in failedNote whether the person was charged.
- Keep the plan-locked state honest: say which plan has stems and that the full song can still be exported.
Built toward WCAG 2.2 AA and checked automatically; no independent audit yet. Read the accessibility guide