Adding a song to 4Stem Band Player is a fully offline, script-driven process. There is no upload UI — you place files in the right folder, run a couple of npm scripts, and the song becomes part of the static catalog. This guide walks through every step from folder creation to the final release check you should run before committing or deploying.Documentation Index
Fetch the complete documentation index at: https://mintlify.com/cocreating/4StemPlayer/llms.txt
Use this file to discover all available pages before exploring further.
The Ingestion Steps
Create a new subfolder under
static/songs/ using a stable, URL-safe name with no spaces. The folder name becomes the song’s unique ID in the manifest and in all stem URLs.Avoid spaces.
My New Song would require URL encoding everywhere it appears and is not supported as a folder name. If your song title contains spaces, use a CamelCase or concatenated slug for the folder and set the title field in song.json to the full display name.{
"title": "My New Song",
"artist": "My Band",
"key": "A minor",
"bpm": 120,
"timeSignature": "4/4"
}
Add optional fields as needed. A fully specified example with chords, sections, and duration metadata:
{
"title": "My New Song",
"artist": "My Band",
"key": "A minor",
"bpm": 120,
"timeSignature": "4/4",
"duration": "4:32",
"durationSeconds": 272,
"chords": {
"intro": {
"progression": "Am | F | C | G"
},
"verse": {
"label": "Verse",
"progression": "Am | F | C | G",
"notes": "Repeat twice before chorus."
},
"chorus": {
"progression": "F | G | Am | Am"
}
},
"notes": "Rehearsal notes visible in the metadata panel.",
"sections": [
{ "label": "Intro", "start": 0 },
{ "label": "Verse", "start": 8 },
{ "label": "Chorus", "start": 32 }
]
}
See the song.json reference for the full schema and validation rules.
Create
static/songs/<Folder>/lyrics.md. The file must exist — validation will report an error if it is missing. An empty file is accepted with a warning, so you can create a placeholder now and fill it in later:Place the required stem files in the song folder. The three required stems are
bass, drums, and vocals. Optional stems (guitar, strings, fx, other) are loaded dynamically if their files are present.static/songs/MyNewSong/MyNewSong_bass.mp3
static/songs/MyNewSong/MyNewSong_drums.mp3
static/songs/MyNewSong/MyNewSong_vocals.mp3
static/songs/MyNewSong/MyNewSong_guitar.mp3 ← optional
<Folder>_<stem>.mp3 (e.g. MyNewSong_bass.mp3)<SongTitle>_<stem>.mp3 (e.g. My New Song_bass.mp3)_<stem>.mp3All required files must be present and non-empty. Any optional file found in the folder must also be non-empty.
Once the folder is populated, run the preparation script to validate, generate waveform peaks, and rebuild the manifest:
song.json (skip with --skip-bpm-detect to preserve a manually curated value)..peaks.json waveform data files for any stem that doesn’t have one yet, or whose MP3 has changed since the peak file was last written.static/songs/manifest.json to include the new song.If validation fails, the output will list each error. Fix the reported issue and run the command again.
songs:prepare calls songs:peaks internally, which requires ffmpeg on your PATH. If ffmpeg is not installed, peak generation will fail with Peak generation requires ffmpeg to be available on PATH. Install ffmpeg and rerun, or pass --skip-peaks to skip peak generation for now.If you only need to refresh peak files or regenerate the manifest without letting the script rewrite your carefully tuned BPM value, pass
--skip-bpm-detect:npm run check — TypeScript and Svelte diagnosticsnpm test — unit test suitenpm run build — full static production buildRemoving an Optional Stem
To remove an optional stem (for example,guitar) from an existing song:
- Delete the stem MP3:
static/songs/<Folder>/<Folder>_guitar.mp3 - Delete its peak file:
static/songs/<Folder>/<Folder>_guitar.peaks.json - Regenerate the catalog:
Troubleshooting
Error: "missing <stem> stem mp3"
Error: "missing <stem> stem mp3"
The folder does not contain one of the required stem files (
bass, drums, or vocals). Check that the file exists and that its name ends with _bass.mp3, _drums.mp3, or _vocals.mp3. The resolution order is <Folder>_<stem>.mp3 → <SongTitle>_<stem>.mp3 → any file ending in _<stem>.mp3. Rename the file to match one of those patterns and rerun songs:prepare.Error: "song.json: missing <field>"
Error: "song.json: missing <field>"
A required field (
title, artist, key, bpm, or timeSignature) is absent or empty in song.json. Open the file and add the missing field, then rerun songs:prepare.Error: "bpm must be a number"
Error: "bpm must be a number"
The
bpm field is present but written as a JSON string. Change it from "120" to the bare number 120:Error: "Peak generation requires ffmpeg to be available on PATH"
Error: "Peak generation requires ffmpeg to be available on PATH"
The
songs:peaks script calls ffmpeg as an external process and it was not found. Install ffmpeg for your platform (e.g. brew install ffmpeg on macOS, or download from ffmpeg.org), ensure it is on your PATH, and rerun npm run songs:prepare.Waveforms don't update after replacing an MP3
Waveforms don't update after replacing an MP3
By default,
songs:peaks skips a stem’s peak file if it is newer than the MP3. If you replaced an MP3 but the peak file timestamp wasn’t reset, pass --force-peaks to regenerate all peaks unconditionally: