Beatmaps
Create, inspect, import, and export beatmaps and their storyboards.
create-storyboard
Builds a beatmap and storyboard from three images, then imports the finished set.
cd examples/create-storyboard
npm install
npm startOpen the Realm together with its files folder. This example writes both database records and file content, so it does not use read-only mode.
const osu = init(realmPath, { schemaVersion: 51, filesFolderPath: filesPath })
Read the three images and put each buffer in the content-addressed file store. The returned hash is what the beatmap set keeps in Realm.
const [bgBuf, logoBuf, spBuf] = [readFileSync(bgPath), readFileSync(logoPath), readFileSync(spPath)]
const images = [
{ filename: bgPath.split(/[\\/]/).pop(), content: bgBuf, hash: osu.files.put(bgBuf).hash },
{ filename: logoPath.split(/[\\/]/).pop(), content: logoBuf, hash: osu.files.put(logoBuf).hash },
{ filename: spPath.split(/[\\/]/).pop(), content: spBuf, hash: osu.files.put(spBuf).hash },
]
A storyboard is split into layers. Here, every sprite goes into the foreground layer. Commands can be chained, which keeps a short animation readable without hiding its timing.
const sb = new Storyboard()
const fg = sb.getLayer("Foreground")
const bg = new StoryboardSprite(images[0], Anchor.Centre, { x: 320, y: 240 })
bg.addAlpha(Easing.None, 0, 8000, 1, 0).addScale(Easing.Out, 0, 1000, 1.1, 0.9)
fg.add(bg)
const logo = new StoryboardSprite(images[1], Anchor.Centre, { x: 320, y: -50 })
logo.addAlpha(Easing.In, 500, 1500, 1, 0).addMoveY(Easing.Out, 1500, 3000, 240, -50)
fg.add(logo)
const sp = new StoryboardSprite(images[2], Anchor.Centre, { x: 100, y: 100 })
const loop = sp.addLoopingGroup(2000, 5)
loop.addAlpha(Easing.None, 0, 400, 0.8, 0).addScale(Easing.None, 0, 400, 1.5, 0.5)
fg.add(sp)
Serialize the plain beatmap object back to .osu text, then store that text like any other file.
const osuContent = osu.beatmap.serialize(beatmap)
const osuHash = osu.files.put(Buffer.from(osuContent)).hash
Register the set after all of its files have hashes. importSet writes the set, beatmap, and file references in one operation.
const result = osu.sets.importSet({
onlineID: -1,
setHash,
status: 0,
protected: false,
files: allFiles,
beatmaps: [{
filename: osuFile,
hash: osuHash,
md5Hash: createHash('md5').update(Buffer.from(osuContent)).digest('hex'),
osuBeatmap: beatmap,
}],
})
API used: init · OsuFilesAPI#files · OsuFilesAPI#beatmap · OsuFilesAPI#sets · Storyboard · StoryboardSprite · StoryboardLoopingGroup · Anchor · Easing
explore-storyboard
Opens a beatmap and lets you browse its storyboard one layer and element at a time.
cd examples/explore-storyboard
npm install
npm startStart with the available sets. The labels use the first beatmap's metadata so the picker shows an artist and title instead of a Realm ID.
const sets = osu.sets.get.map(s => ({
item: s,
label: `${s.Beatmaps?.[0]?.Metadata?.Artist} - ${s.Beatmaps?.[0]?.Metadata?.Title} (#${s.OnlineID})`
}))
Realm only stores the beatmap record. getFullData also reads and parses the .osu file, which is where the storyboard lives.
const data = osu.beatmap.getFullData(String(beatmap.ID))
if (!data?.storyboard) { console.log("No storyboard on this beatmap."); return }
const sb = data.storyboard
NOTE
Query results are detached, read-only snapshots. Use osu.sets.open(id) or osu.beatmap.save(...) when you need to keep an edit.
Each layer contains sprites, animations, and samples. Pick an element to print its commands, including commands nested in loops and triggers.
const layers = [...sb.layers.entries()].map(([name, layer]) => ({
item: { name, layer },
label: `${name} (${layer.elements.length} elements)`,
}))
const picked = await pick(layers, `Layers - ${beatmap.DifficultyName} (q=quit)`)
API used: init · OsuFilesAPI#sets · OsuFilesAPI#beatmap · OsuBeatmap · Storyboard · StoryboardLayer · StoryboardCommandGroup
export-newest-osz
Exports the most recently added beatmap set as an .osz archive.
cd examples/export-newest-osz
npm install
npm startSort the sets by DateAdded and take the first result. If Realm is empty, there is nothing to export.
const newest = initialized.sets.get.sortedBy('DateAdded').first()
if (!newest) {
console.log('No beatmap sets found.')
return
}
Export by Realm ID. The exporter reads the beatmaps and their referenced files, then packs them into one .osz archive.
await initialized.osz.export(newest.ID.toString(), outputPath)
API used: init · OsuFilesAPI#sets · OsuFilesAPI#osz
import-beatmap
Imports every .osz in a folder and deletes each archive after a successful import.
cd examples/import-beatmap
npm install
npm startRead the chosen directory and keep only files with an .osz extension. checkHash makes the file store verify content before accepting it.
const allFiles = await readdir(beatmapDir)
const oszFiles = allFiles.filter(f => extname(f).toLowerCase() === '.osz')
Import one archive at a time. A successful import returns the set ID and its beatmaps; only then does the example remove the original archive.
const result = await initialized.osz.import(fullPath)
console.log(` OK: setID=${result.onlineID} beatmaps=${result.beatmaps.length}`)
await rm(fullPath)
API used: init · OsuFilesAPI#osz · BeatmapSetData