MP3 to xLights Sequencer
Analyzes your music, detects beats/onsets/chords/sections, groups your layout props, and applies themed effects — all driven by the audio.
- Audio Analysis — Analyzes MP3 files using 17 algorithms across 7 hierarchy levels (L0-L6) to extract beats, onsets, chords, sections, energy curves, and stem separation (drums/bass/vocals/guitar/piano/other)
- Layout Grouping — Reads your
xlights_rgbeffects.xmland auto-generates 8-tier Power Groups (spatial, rhythmic, prop type, compound, heroes) - Effect Library — 40 xLights effects cataloged with parameters, prop suitability ratings, and analysis-to-parameter mappings
- Variant Library — 212+ pre-tuned effect variants with contextual tags (energy, tier, section role, genre) for quick effect selection
- Theme Engine — 21 composite "looks" (Inferno, Aurora, Winter Wonderland, etc.) organized by mood, occasion, and genre
- Song Story — Automatic section classification with lyric-anchored boundary refinement (syncedlyrics), energy arcs, and lighting moment detection
- Sequence Generation — Produces
.xsqfiles ready to import into xLights with effects placed by tier, energy, and theme - Web UI — Browser-based dashboard for the full workflow: upload, analyze, review, edit themes, browse variants, group layout, and export
Runs anywhere Docker does — Windows, macOS, or Linux. The only prerequisite is Docker Desktop (or Docker Engine on Linux) — docker.com.
git clone https://github.com/derwin12/xlights-autosequencer.git
cd xlights-autosequencer
docker compose upOpen http://localhost:5000. First run builds the toolchain image (the Vamp plugins compile from source — the slow step, easily 20–40 min, but fully unattended) and installs the Python/JS packages; later runs are fast. Your song library and cached analysis persist in named Docker volumes across restarts.
Once the page loads, the first-run flow is: drop an MP3/WAV onto the
Drop tab and let the analysis pipeline run. The xLights layout is
fixed — every song exports against the xlights_rgbeffects.xml and
xlights_networks.xml committed at layout/, so there's no
per-song layout import step. From there walk the numbered tabs
left-to-right and finish with a Download Package on the Export tab — the
full walkthrough is in Launch the App and
The screens below.
The footer of every screen shows ui <commit> · built <date> · api <commit> —
when either commit falls behind main, update like this:
git pull
docker compose exec xonset sh -c "cd src/review/frontend && npm run build"
docker compose restartThen hard-refresh the browser (Ctrl+Shift+R). What each step covers:
git pull— the app code runs from your checkout (bind-mounted into the container), so pulling is most of the update.- The
npm run buildline rebuilds the web UI. Only needed when frontend files changed (src/review/frontend/), but it's fast and always safe — the container skips rebuilding it on startup whenever a built bundle already exists, so don't rely on a restart alone to pick up UI changes. docker compose restart— restarts the Python backend so pulled backend changes take effect (it doesn't hot-reload). Expect a couple of quiet minutes at[1/5]while pip re-checks dependencies.
Only if .devcontainer/Dockerfile itself changed (new system-level
dependency — rare) do you need a real rebuild: docker compose up -d --build.
Everything you upload or generate (song library, cached stems/analysis,
image library, custom themes) lives under ~/.xlight/ inside the
container, backed by named Docker volumes — it survives restarts,
recreates, and image rebuilds, and isn't tracked in git. To carry it to
another machine, docker cp it out of/into the xlight-state volume.
Contributors:
.devcontainer/also works as a VS Code Dev Container for interactive development inside the same toolchain image. Note it runs an outbound-traffic firewall on start (built for sandboxing an AI coding agent) — normal internet access outside its allowlist is blocked by design. Seescripts/startapp.shfor restarting the server after backend changes.
docker compose up already started the app — open http://localhost:5000.
The whole workflow lives in seven tabs across the top of every screen:
xLightsAI 1 Library 2 Import 3 Analyze 4 Timeline 5 Theme 6 Extras 7 Export
The numbers indicate the natural order — drop a song in, walk through the tabs left-to-right, finish with an .xsq ready for xLights.
Where you start the very first time.
- Tab bar (top). Numbered steps; the active tab is underlined orange. You can revisit any tab at any time.
- Drop zone (centered card). Drag an MP3 / WAV file — or a video file (mp4/mov/avi/mkv/webm), whose audio track is extracted automatically — onto it, or click "or click to browse files" to open a native file picker. Both mono and stereo audio are accepted; ID3 metadata is read automatically.
That's the entire screen — the app is deliberately empty here so the call to action is unmissable.
After you drop a file, the app jumps straight to Analyze and starts the pipeline.
- Title bar — "Analyzing...
<slug>·<duration>" on the left, "<elapsed>/ ~<eta>" and a skip to timeline → escape hatch on the right. The skip button shows up once enough has been detected to render something in the timeline. - Artist / Title fields — read from the MP3's ID3 tags. Edit either to override what's used for the synced-lyrics lookup.
- Phase pills — seven logical phases (loading audio → separating stems → tracking beats → finding bars → segmenting structure → song story → assigning themes). The active phase is outlined; completed phases get a green checkmark.
- Detectors column (left) — every algorithm the pipeline will run, in execution order. Each row shows status (queued / running / done), library tag (system, demucs, librosa, vamp, madmom), and progress.
- Stream column (middle) — live SSE log lines from the pipeline. Mostly the same information as the Detectors column but in narrative form, with elapsed time per detector.
- Findings column (right) — overall progress %, ETA, list of high-level outputs (waveform, beats, bars, sections, themes), and a live-growing Sections list as the structure detector finds boundaries.
- Song rail (far left, collapsible). Songs you've imported, grouped by folder. Switch between songs by clicking; the right side switches to whichever song you pick.
Same screen, after the pipeline finishes (~60–90 s on a typical song with cached stems).
- Title bar flips to "Analysis complete" with a prominent ▶ review timeline → button on the right.
- All seven phases show green checkmarks.
- Detectors — full list (33 / 33 done), each with its detected mark count visible (e.g.
librosa_beats · 288,aubio_onset (drums) · 700 marks). - Stream — the full progress log, scrollable.
- Findings — 100 %, with per-category counts (
waveform ✓,beats 459,bars 80,sections 5,themes ✓) and the actual section list with role + duration (01 Verse · 20s,02 Pre Chorus · 19s, ...). - Re-analyze button (bottom left) — re-runs the pipeline from scratch, ignoring any cache. Useful after a story-builder schema bump, or when you've edited the artist/title fields and want a fresh synced-lyrics lookup.
The most-used screen. Verifies the analysis matched what you hear.
- Transport (top center) — ⏮ ▶ ⏭ play/scrub controls, current position / total duration.
- Zoom controls —
−/+buttons; reads as1×,2×, etc. Higher zoom narrows the visible window so you can scrub onto a single beat. - Waveform — full-mix audio rendered as a green stereo waveform, with a 0:00–total-duration time ruler underneath. Click anywhere on it to seek the playhead.
- Sections row — colored boxes per detected section, labeled by role (Verse, Pre Chorus, Chorus, Bridge, Outro, Interlude, etc.). Click "Edit sections" (button on the right) to adjust boundaries by dragging.
- Section beat counter — small dots showing the bar/beat structure within the currently-visible window.
- Stem waveforms (collapsible) — drums / bass / vocals / guitar / piano / other waveforms stacked. Click "click to load" to render them; useful for verifying the stem separation looks right.
- Raw algorithm tracks — every individual detector's output as a flash-when-the-event-passes tick row. Each row shows the algorithm name, the event count, and a sparkline. Toggle visibility per-row to declutter; the "31 / 31 visible" counter updates.
- AYHEAD inspector (right column) — current playback position formatted as
bar X · beat Y of Z. - Current section — name and color of whatever section the playhead is in right now. Updates as you scrub.
- Section timing — start / end / duration of the current section.
- Nudge buttons —
−10 ms/+10 msto micro-adjust the active section's start time. Hold-and-drag for repeated nudges. - Go to Theme → (bottom) — finishes timeline review and moves to the next step.
Pick a theme (composite lighting "look") for each section of the song. Themes encode color palette, effect choice, blend modes, and parameter mappings.
- Section navigator (top) — every detected section as a clickable pill (Verse / Pre Chorus / Chorus / Verse / Outro). The active pill outlines orange; click to switch which section you're theming.
- Accept All Defaults (top right) — auto-assigns theme defaults to every section using the song's energy/genre profile. Good starting point if you don't want to pick one-by-one.
- Theme grid (center) — each card is one theme:
- Palette swatch strip (5 colors) at the top.
- Theme name (e.g. Aurora, Inferno, Stellar Wind).
- Mood tags (e.g. ETHEREAL, AGGRESSIVE, ROCK, DARK) for quick filtering.
- One-line description — what the theme evokes. Click a card to assign it to the active section.
- Section beat strip (right of the grid) — visualizes the currently-selected section's beat structure, labeled with the section name.
- Section parameters (right column) — four sliders that fine-tune this section's render of the chosen theme:
- Brightness — global intensity (0 – 1)
- Hit Strength — accent emphasis on beats (0 – 1)
- Dwell Time — how long held effects last (0 – 1)
- Color Shift — palette rotation (0 – 1)
Per-section overrides are remembered when you switch sections; the Accept All Defaults button resets them.
Optional per-song lyric-word triggers for two effects: image accents on Matrix/Mega Tree props (Pictures), and a two-layer drop-shadow word effect (Shadow Text). Nothing here is required — skip straight to Export if you don't want either.
- Suggested topics — lyric words from this song that don't have a
matching image in your shared image library yet. Per word:
- Create image — opens a pre-filled AI image-generation prompt you can copy into Gemini (or any image generator), styled to match the catalog's flat-icon look.
- Choose image — upload an image file directly for this word.
- Shadow — tags the word for the Shadow Text effect. Click again to untag (shows Shadow ✓ while active).
- A previously unmapped word shows an "unmapped from
<file>" note and a Restore match button instead.
- Already matched — words already resolved to a library image, shown
as
"word" → filename.png. Same Create image / Choose image / Shadow buttons, plus Unmap — suppresses the Pictures effect for that word in this song only (the library image itself isn't deleted, and stays available for other songs). - Moving Head Triggers — a separate list of lyric words that fire a Moving Head accent when sung, independent of Pictures/Shadow. Three built-ins (shake, bounce, spin) are on by default; uncheck (Remove) to disable one for this song. Add your own word either from the "Add from this song's lyrics" picker or the custom-word box at the bottom, assigning it one of four motions: shake, bounce, spin, or flash (points every head straight up at full white).
At generation time, Pictures cycles catalog images in as an overlay on Matrix/Mega Tree props, timed to each lyric match, with a short pan and an occasional zoom/rotation flourish. Shadow Text renders a tagged word itself as two stacked layers on the same props whenever it's sung — the word in the song's main palette color on top, a slightly offset copy in a second palette color directly behind it — using the same pan/zoom/rotation movement Pictures bursts use. Both are optional accents layered on top of whatever the Theme tab already assigned.
The terminal step. Generates the xLights .xsq sequence from the analyzed song + assigned themes + the repo's committed prop layout.
- Generate button — produces the
.xsq. - Layout summary — number of models / groups / props detected from
layout/xlights_rgbeffects.xml, and which 8-tier Power Groups were auto-generated (heroes, compounds, props, beats, etc.). - Generation options — variation seed, repetition policy, palette restraint, duration scaling. Most users leave defaults.
- Download Package — bundles the generated
.xsqwithlayout/xlights_rgbeffects.xmlandlayout/xlights_networks.xmlinto a single.xsqz(xLights' own zipped-sequence-package extension) ready to import directly into xLights.
Where you go to switch between songs once you've imported a few.
- Filter pills (top) — All / Draft / Analyzed / Themed. Filters the song list by status. Draft = imported but not analyzed; Analyzed = pipeline complete; Themed = at least one section has a theme assignment.
- Folder groups — songs are bucketed by
folder_id(defaultunfiled). The count next to each folder name shows how many songs are in it. Click the chevron to collapse / expand. - Song row — each row shows title, artist (from ID3 or override), and a status badge (
Analyzed,Themed,Draft). Click anywhere on the row to open it in whatever tab you visit next. - Song rail (far left) — a permanent compact list across every screen, so you can switch between songs without leaving the current step.
The UI workflow writes these next to the source MP3:
song.mp3
song/
├── song_hierarchy.json # Analyzer output (L0–L6 hierarchy)
├── song_story.json # Story builder output (sections, roles, moments)
└── song.xsq # Sequence to import into xLights
.stems/<md5>/ # Cached Demucs stem separation (drums, bass, vocals, guitar, piano, other)
_hierarchy.json and _story.json are caches — re-running the pipeline reads them when nothing relevant has changed. .xsq is the deliverable.
In xLights: File → Open Sequence, point at song.xsq. The .xsq is self-contained; xLights will resolve effects and timing from the file.
src/
├── analyzer/ # Audio analysis pipeline
│ ├── audio.py # MP3 loading via librosa
│ ├── result.py # Data classes (TimingTrack, HierarchyResult, etc.)
│ ├── runner.py # Orchestrates algorithm runs
│ ├── orchestrator.py # Hierarchy assembly (L0-L6)
│ ├── scorer.py # Quality scoring
│ ├── stems.py # Demucs stem separation (6 stems)
│ ├── phonemes.py # WhisperX phoneme analysis
│ ├── xtiming.py # .xtiming XML writer
│ ├── xvc_export.py # .xvc value curve writer
│ ├── pipeline.py # End-to-end export pipeline
│ └── algorithms/ # 17 algorithm implementations (librosa, vamp, madmom, essentia)
├── grouper/ # xLights layout -> Power Groups
│ ├── layout.py # Parse xlights_rgbeffects.xml
│ ├── classifier.py # Normalize, classify, detect heroes
│ ├── grouper.py # 8-tier group generation
│ └── writer.py # Inject groups back into XML
├── effects/ # xLights effect catalog
│ ├── builtin_effects.json # 35 effect definitions
│ ├── models.py # EffectDefinition, EffectParameter, AnalysisMapping
│ └── library.py # Load, query effects
├── variants/ # Pre-tuned effect presets
│ ├── builtins/ # 34 per-effect JSON files (123+ variants)
│ ├── models.py # EffectVariant, VariantTags
│ ├── library.py # Load, query, save custom variants
│ ├── scorer.py # Context-aware variant scoring
│ └── importer.py # Import variants from .xsq files
├── themes/ # Composite effect themes
│ ├── builtin_themes.json # 21 theme definitions
│ ├── models.py # Theme, EffectLayer (with variant_ref)
│ └── library.py # Load, query by mood/occasion/genre
├── story/ # Song story builder
│ ├── models.py # SongStory, Section, Moment, MoodCurve
│ ├── builder.py # Build story from hierarchy + lyric-anchored refinement
│ ├── section_classifier.py # Detect verse/chorus/bridge/etc.
│ ├── energy_arc.py # Energy curve computation
│ └── lighting_mapper.py # Map story to lighting cues
├── generator/ # Sequence generation
│ ├── models.py # GeneratorConfig, SequencePlan, EffectPlacement
│ ├── plan.py # Generate effect placement plan
│ ├── theme_selector.py # Theme selection by mood/occasion
│ ├── effect_placer.py # Place effects on props (resolves variant_ref)
│ ├── value_curves.py # Dynamic parameter changes
│ └── xsq_writer.py # Write .xsq sequence XML
├── review/ # Web UI
│ ├── server.py # Flask app (dashboard, upload, timeline, export)
│ ├── theme_routes.py # Theme CRUD API + editor page
│ ├── variant_routes.py # Variant library API + browser page
│ ├── story_routes.py # Story review API
│ └── static/ # HTML, CSS, JS (vanilla, no build step)
├── cli.py # Click CLI entry point (30+ commands)
├── cache.py # MD5-keyed analysis cache
├── library.py # ~/.xlight/library.json song index
└── export.py # JSON serialization
| Path | Contents |
|---|---|
~/.xlight/library.json |
Song library index |
~/.xlight/custom_themes/*.json |
Custom theme overrides |
~/.xlight/custom_variants/*.json |
Custom effect variants |
~/.xlight/sweep_configs/ |
Parameter sweep configs |
.stems/<md5>/ |
Cached stem separation output (adjacent to source audio) |
| Issue | Fix |
|---|---|
TorchCodec is required warning |
Harmless — can be ignored |
| Stem separation slow on first run | Normal — demucs downloads ~200 MB model. Cached after first run. |
| whisperx alignment model fails | docker compose exec xonset sh -c "pip install huggingface_hub && huggingface-cli login" |
docker compose exec xonset pytest tests/ -vDetailed docs for each subsystem are in docs/.
This project is licensed under the MIT License — see LICENSE for details.






