assets/ directory. The engine merges your mod into the base game by prepending your directory to the asset search path — your files take precedence, base game files are the fallback.
Directory layout
Asset override priority
When the engine resolves any asset, it walks the search roots in order and returns the first match:JSON files are not merged. Overriding
characters/bf.json replaces the entire file — you must include all fields, not just the ones you want to change.Charts
Charts live indata/{song-name}/. The chart filename determines the difficulty:
| Filename | Difficulty |
|---|---|
{song-name}.json | Normal (default) |
{song-name}-easy.json | Easy |
{song-name}-hard.json | Hard |
data/{song_name}/{filename} across all search roots. Your mod’s version is found first if you place it in the right path.
An optional events.json file in the same directory can hold events separately from the chart:
Characters
Character JSON files go incharacters/. The engine resolves them via:
image field in the character JSON points to the spritesheet path relative to images/ (without extension). The engine then looks for both the .png and .xml files in the images/ directory across all search roots.
See Asset formats — Characters for the full field reference.
Stages
Stage JSON files go instages/. The engine resolves them via:
stages/{name}.lua, it is loaded as the stage script when the song starts.
Stage images are resolved using the stage’s directory field. The engine checks {directory}/images/{image}.png before falling back to images/{image}.png.
See Asset formats — Stages for the full field reference.
Lua scripts
Song scripts
Any.lua file inside data/{song-name}/ is loaded as a song script. You can have multiple scripts per song — they are all loaded and every callback is called on all of them.
The engine discovers song scripts at runtime:
script.lua— General song scriptmodchart.lua— Note manipulation and camera effectseventScript.lua— Custom event handling
Stage scripts
A stage Lua script is loaded fromstages/{name}.lua when the corresponding stage is active. Stage scripts receive the same callbacks as song scripts.
Custom event scripts
Scripts incustom_events/ are loaded globally and are always active. They receive onEvent calls for every event in every song:
Script loading order
Custom event scripts load
All
*.lua files in custom_events/ across all search roots are loaded, sorted alphabetically.Sounds and music
| Asset type | Path |
|---|---|
| Song instrumental | songs/{song-name}/Inst.ogg |
| Song vocals | songs/{song-name}/Voices.ogg |
| Custom vocals file | songs/{song-name}/{vocals_file}.ogg (set in character JSON) |
| Sound effects | sounds/{name}.ogg |
| Background music | music/{name}.ogg |
Images
Images are loaded fromimages/ in each search root. The engine looks for:
| Asset type | Path |
|---|---|
| Sprite atlas PNG | images/{path}.png |
| Sprite atlas XML | images/{path}.xml |
| Health icon | images/icons/icon-{name}.png or images/icons/{name}.png |
| Stage image | images/{image}.png (or {directory}/images/{image}.png) |
Song discovery
The engine discovers available songs by scanningdata/ directories across all search roots. A folder is treated as a song if it contains a chart file matching the folder name:
Weeks
Week definitions live inweeks/{name}.json. They control which songs appear in Story Mode and Freeplay. The engine loads weeks from the first weeks/ directory it finds, then merges from additional roots.
songs is [song_name, character, [r, g, b]] where character is the character shown in the story mode menu for that song, and the color array is the album art background color.