Before building, ensure you have the following installed on your system:
- Rust: Install via rustup.
- Vulkan SDK: Download and install the SDK for your operating system from the LunarG website.
- CMake: Install from cmake.org.
- Ninja: Install from ninja-build.org.
Use the Windows 7 target so unsupported Windows APIs are excluded:
rustup toolchain install nightly --component rust-src
cargo +nightly build -Z build-std=std,panic_abort --release --locked --target x86_64-win7-windows-msvc --bin deadsync
scripts/check-win7-imports.ps1 -Path target/x86_64-win7-windows-msvc/release/deadsync.exeFor 32-bit builds, substitute i686-win7-windows-msvc. The import check uses
dumpbin from the Visual Studio C++ tools and also runs before Windows 7 releases
are packaged. It checks for WinRT startup dependencies; testing on Windows 7 is
still needed to verify runtime compatibility.
Bluetooth heart-rate monitors are unavailable in Windows 7 builds because their Windows backend requires Windows 10 or later.
sudo apt update
sudo apt install --no-install-recommends build-essential clang cmake pkg-config libclang-dev libdbus-1-dev libudev-dev libasound2-dev libpipewire-0.3-dev libvulkan-dev libgl1-mesa-devInstall the toolchain and native libraries:
xcode-select --install # C/C++ toolchain, Git, Python (needed by shaderc-sys)
brew install cmake vulkan-loader molten-vk- CMake — required to build
shaderc-sys(the shader compiler) from source. - vulkan-loader — the
libvulkanthe engine links against and loads at runtime. - molten-vk — the Vulkan-to-Metal driver that makes Vulkan actually run on Apple GPUs.
- The Xcode Command Line Tools provide the compiler/linker plus the Git and
Python that
shaderc-sysinvokes during its build.
DeadSync also includes a direct native Metal renderer that does not use
wgpu. Choose Metal under Graphics > Video Renderer to use it, or choose
Metal (wgpu) to keep using wgpu's Metal backend. The native renderer itself
needs only Apple's system Metal framework; the Homebrew Vulkan packages above
remain necessary while the Vulkan backend is part of the default build.
You do not need the LunarG Vulkan SDK for a normal build/run on macOS — the
Homebrew loader + MoltenVK are enough. Install the SDK only if you want the
Vulkan validation layers for debugging (GfxDebug in deadsync.ini).
The repo ships a .cargo/config.toml that adds the Homebrew Vulkan loader to
the link and runtime search paths for macOS, so no manual environment variables
are required on either Apple Silicon (/opt/homebrew) or Intel (/usr/local):
cargo run # fast dev build
cargo run --profile local # optimized, but faster to link than full release
cargo build --release # fully optimized (LTO) build for benchmarking/playBefore the first run, grant Input Monitoring to your terminal (see the macOS run note under Getting Started) or the game won't receive keystrokes.
If you use a non-standard Homebrew prefix and linking still fails with
library 'vulkan' not found, export the paths for your prefix:
brew_prefix="$(brew --prefix)"
export LIBRARY_PATH="$brew_prefix/lib:$brew_prefix/opt/vulkan-loader/lib:${LIBRARY_PATH:-}"
export DYLD_FALLBACK_LIBRARY_PATH="$brew_prefix/lib:$brew_prefix/opt/vulkan-loader/lib:${DYLD_FALLBACK_LIBRARY_PATH:-}"
export RUSTFLAGS="-L native=$brew_prefix/lib -L native=$brew_prefix/opt/vulkan-loader/lib"pkg install cmake python3 pkgconf alsa-lib alsa-plugins vulkan-validation-layers hidapiTest fixtures live in deadsync-tests,
mounted as a Git submodule at tests/fixtures. Initialize the pinned fixture
revision before compiling or running tests, examples, or benchmarks (Python
3.11 or newer is required to restore split archives):
git submodule update --init tests/fixtures
python scripts/prepare_test_fixtures.pyNormal game builds do not need the fixture submodule.
Archives over 50 MB are stored as parts of at most 48,000,000 bytes. The Python command verifies each part and restores the exact archives locally; restored files are ignored by Git. Run both commands again after fixture updates.
When updating fixtures, commit and push changes in tests/fixtures first, then
commit the updated submodule pointer in this repository. Test runners and fixture
maintenance scripts stay in this repository; their existing paths still work.
Run a full song Lua fixture with live progress and a final comparison tally:
cargo test --test full_song_lua Accendio
cargo test --test full_song_lua -- --listFixtures are consolidated under tests/fixtures/full_song_lua and compressed at
zstd level 22. See its fixture guide for
selection, corpus runs, and reference maintenance.
Follow these steps to get the game running:
-
Clone the Repository:
git clone https://github.com/pnn64/deadsync.git cd deadsync -
Add Songs: Place your song packs in one of DeadSync's song scan roots:
- the
songs/folder inside the data directory (see Data Directories) - the
songs/folder next to the executable - any folder listed in
AdditionalSongFolders,AdditionalSongFoldersReadOnly, orAdditionalSongFoldersWritable
Example structure inside a song root:
<song-root>/MyPack/MySong/MySong.ssc - the
-
Build the Project: Compile the game in release mode for optimal performance:
cargo build --release
-
Run the Game: After a successful build, run the executable from the project root:
- On Windows:
.\target\release\deadsync.exe
- On Linux:
./target/release/deadsync
- On macOS:
Before the first run, grant Input Monitoring permissions to
Terminal.appinSystem Settings > Privacy & Security > Input Monitoring. Without this, deadsync will not receive any keystrokes. Then, run:./target/release/deadsync
- On Windows:
Run this from your DeadSync installation directory:
.\deadsync.exe --updateOn Linux, FreeBSD, and macOS, use ./deadsync --update.
The command checks for the latest release, downloads and verifies the matching build, installs it, and exits. It prints progress without opening a game window, loading songs, or starting audio, and leaves the game closed after the update. If your version is already current or newer, it exits without downloading.
Exit status is 0 on success (including already up to date), or 1 on failure.
In PowerShell scripts, use Start-Process .\deadsync.exe -ArgumentList '--update' -NoNewWindow -Wait -PassThru and read the returned process's ExitCode.
--update takes precedence over --no-update-check. Installations with
UpdaterInstallEnabled=0 must use their package manager or distribution's updater.
After running the game for the first time, configuration files and a save directory will be generated.
You can edit deadsync.ini to change various settings, including renderer, video resolution, VSync, GfxDebug (backend validation/debugging), and the default theme color.
You can fully customize keyboard and gamepad controls in the [Keymaps] section of deadsync.ini. DeadSync maps virtual actions (e.g. P1_Up, P1_Start, P1_Back) to one or more physical inputs.
Use KeyCode::<Name> values, for example:
KeyCode::ArrowLeft,KeyCode::ArrowRightKeyCode::KeyA,KeyCode::KeyS,KeyCode::KeyD,KeyCode::KeyW
Example:
[Keymaps]
P1_Up=KeyCode::ArrowUp,KeyCode::KeyW
P1_Start=KeyCode::Enter
P1_Back=KeyCode::EscapeGamepad/pad bindings are based on DeadSync’s PadCode[...] values emitted by the native input backend. The recommended way to bind a button is:
PadCode[0xDEADBEEF]— bind to any gamepad button with that raw code.PadCode[0xDEADBEEF]@0— bind to that code, but only on gamepadID 0(as shown in logs / sandbox).
To discover the codes for your device:
- Start the game and go to the Sandbox screen by pressing
F4. - Press buttons on your controller; you will see lines like:
Gamepad 0 [uuid=...]: RAW BTN { PadCode[0x00030030], ... }
- Copy the
PadCode[...]part (and optionally the@0device index) intodeadsync.ini.
Example: bind P1 Start/Back to a specific button on gamepad 0:
[Keymaps]
P1_Start=PadCode[0x00030030]@0
P1_Back=PadCode[0x00030031]@0Legacy high-level bindings like PadDir::Up, PadButton::Confirm, and PadN::Dir::Left are still accepted for convenience, but low-level PadCode[...] bindings are the most accurate and device-agnostic way to configure controllers.
| Key | Action |
|---|---|
Tab (hold) |
Fast-forward menus and transitions at 4×. Disabled in gameplay. |
` (hold) |
Slow menus and transitions to 0.25×. Disabled in gameplay. |
Tab + ` |
Halt menu animations. Disabled in gameplay. |
F3 |
Cycle the stats overlay (off → FPS → +stutter → +timing). |
Ctrl+F3 |
Toggle the live frame-statistics overlay: rolling per-phase frame-time graph (with idle/await-GPU segments and catch-up/spike markers), a frame-interval jitter histogram, and display-clock + audio sync-health readouts. Opens in the top-right corner, or the last position you moved it to. |
Ctrl+Shift+F3 |
Move the frame-statistics overlay to the next corner. The chosen corner is remembered across toggles and restarts. |
Ctrl+Alt+F3 |
Switch the frame-statistics overlay between the detailed presentation (stable decaying-histogram p99 + jitter histogram) and a minimal presentation (the graph is the jitter display — no histogram, no percentiles). Remembered across restarts. |
These shortcuts mirror ITGmania's debug-loop modifiers and never affect timing-sensitive gameplay (note timing, scoring, music sync). Set TabAcceleration=0 in deadsync.ini to disable them entirely.
See Frame-Statistics Overlay for how to read the colored graph, the marker and reference lines, and the readout cells.
A save directory is created inside the data directory to store your personal data (see Data Directories for its location).
- To enable online features with GrooveStats, edit
<data dir>/save/profiles/00000000/groovestats.iniand add your API key and username. This allows the game to fetch your online scores. The SRPG Shop also requiresPassword=<your GrooveStats password>in that file. - You can also change your in-game display name in
<data dir>/save/profiles/00000000/profile.ini.
Lua charts can submit scores when all declared song Lua compiles without errors for the current play, alongside the existing chart allowlist. Missing layers, failed compilation, partial captures, or Lua errors during play do not grant automatic approval. This result is never saved against the chart hash, since the Lua files can change independently of the notes. Other submission rules (such as autoplay, disqualifying modifiers, and course eligibility) still apply.
Golf features are optional and disabled by default. First enable Options > Online Score Services > ArrowCloud Options > Enable Golf Features. This master switch reveals the golf options below. Turning it off hides them and disables golf scoring, displays, and online boards while preserving saved player and Select Music choices.
- Player Options > Advanced Modifiers > Gameplay Extras > Golf Score shows the native stroke counter. It is on by default, so golf packs score as golf as soon as the master switch is enabled. Its Golf Display setting selects golf charts only (charts with an authored par, the default) or all charts. Stroke Feedback enables ACE and penalty flashes. Each player saves these choices independently.
- Player Options > Advanced Modifiers > Results Extras > Golf Results adds a golf evaluation pane, independently of the gameplay counter. It is also on by default and follows the same Golf Display choice.
- Options > Select Music Options > Show Golf Pars enables wheel par badges. Turning the master switch on also turns this on, so pars appear right away; switch it off here if you would rather not see them.
- Options > Select Music Options > GS Box Leaderboards > Golf (ArrowCloud) shows the online event board alongside the other selectable leaderboards, framed in fairway green over the In The Golf course pattern. ArrowCloud sends it with the chart's other boards in the same request, so turning it on or off takes effect immediately.
- Options > Select Music Options > Lead With Event Boards (on by default) lists event boards such as Golf, ITL and SRPG ahead of ITG/EX on charts that are in the event, so the scorebox and leaderboard overlay open on the event before cycling to the other boards. Boards keep the order their service returned, so the service decides between events when a chart is in several.
The native display uses the supplied GolfScoring.lua curved formula: errors
below 5 ms are free, hits are rounded to whole raw strokes individually, and
misses, dropped holds/rolls, and hit mines cost 100 raw strokes each. One displayed
stroke is 1,000 raw strokes; lower is better. Normal ITG/EX/HardEX scoring and
ArrowCloud submissions continue alongside it. The online golf board shows
ArrowCloud's server-calculated event scores, whose formula may differ from the
native display.
Pack authors can place Golf.ini beside the pack's song folders:
[Song Folder]
Par=2.1
[Song Folder:Challenge]
Par=4Section names use the actual song folder name; a difficulty-specific entry takes
precedence over the song-wide entry. Pars are already in displayed strokes.
Golf.ini never enables a feature by itself: the master switch does, after which
every chart with an authored par scores as golf. Metadata refreshes when the
library reloads and when entering Select Music with golf extras enabled.
DeadSync supports zmod-style "drop in a folder, play a random one" custom
sounds. The bundled assets/sounds/ directory ships several folders that
you can fill with your own .ogg files; the data-directory overlay
({data dir}/assets/sounds/...) is honored as well, so you don't have to
modify the install. Files whose name starts with _ are ignored.
| Folder | Plays when |
|---|---|
assets/sounds/evaluation_pass/ |
Any joined player clears (passes) on the Evaluation screen |
assets/sounds/evaluation_fail/ |
All joined players failed on the Evaluation screen |
assets/sounds/evaluation_pb/ |
GrooveStats submit response reports a Personal Best |
assets/sounds/evaluation_wr/ |
GrooveStats submit response reports a World Record (rank 1) |
assets/sounds/song_start/ |
Gameplay starts (first try; not on restart) |
assets/sounds/song_start/restart/ |
Gameplay restarts. Naming is {n}.ogg (1.ogg, 2.ogg, ...). Falls back to restart.ogg for any restart count without a matching file |
{n} in song_start/restart/ is the restart count since the last fresh
entry into Gameplay; it resets to zero on SelectMusic → Gameplay and
between course songs.
Custom per-style menu music works the same way: drop .ogg files into
assets/music/menu/{style}/ (one of hearts, arrows, bears,
ducks, cats, spooky, gay, stars, thonk, technique, srpg9)
and the menu music for that visual style will randomly pick from your
files. If the folder is empty the bundled per-style track plays.
The whole feature is gated by CustomSoundsEnabled in deadsync.ini
(default 1). Set it to 0 to disable all folder-based sound triggers.
DeadSync can communicate directly with dance pad hardware over USB, bypassing the OS gamepad layer. This gives you named panel inputs, stable per-pad player assignment, and the ability to read and write pad sensor configuration from within the game.
| Pad | Status | Docs |
|---|---|---|
| StepManiaX | ✅ Supported | Documentation |
| FSRio | ✅ Supported | |
| Teejusb FSR firmware | Serial threshold configuration; hardware validation pending | Documentation |
| RE:Flex V2 | Native input, per-sensor calibration/thresholds, and lights; hardware validation pending | Documentation |
By default, DeadSync stores user data outside the install directory so that upgrading the game doesn't risk overwriting your config, saves, or scores. Each platform uses native, XDG-compliant locations.
| Platform | Data directory | Cache directory |
|---|---|---|
| Linux / FreeBSD | ~/.local/share/deadsync ($XDG_DATA_HOME/deadsync) |
~/.cache/deadsync ($XDG_CACHE_HOME/deadsync) |
| Windows | %APPDATA%\deadsync |
%APPDATA%\deadsync\cache |
| macOS | ~/Library/Application Support/deadsync |
~/Library/Caches/deadsync |
Data directory contains user data that should be backed up:
deadsync.ini # game configuration
deadsync.log # current run's log (previous runs kept as deadsync-<timestamp>.log)
save/
profiles/ # player profiles, scores, settings
screenshots/ # captured screenshots
songs/ # default song scan root
courses/ # course files
Cache directory contains regenerable data that can be freely deleted:
songs/ # parsed song metadata cache
banner/ # banner image cache
cdtitle/ # CD title image cache
downloads/ # temporary download data
noteskins/ # compiled noteskin cache
unlocks-cache.json # online unlock cache
If you prefer a fully self-contained install (e.g. for arcade cabs or USB sticks), create an empty file named portable.txt next to the executable. When this file is present, DeadSync stores everything in the executable's directory — the same behavior as older versions.
The file's contents are ignored; only its presence matters.
DeadSync scans for songs in the following locations:
- The
songs/folder inside the data directory. - The
songs/folder next to the executable (in non-portable mode, so bundled songs are always found). - Any additional folders listed in
AdditionalSongFolders,AdditionalSongFoldersReadOnly, orAdditionalSongFoldersWritableindeadsync.ini.
Course files follow the same pattern: DeadSync scans the data-directory courses/ root first and, in non-portable mode, also scans the courses/ folder next to the executable.
On first run in non-portable mode, if DeadSync finds a deadsync.ini next to the executable but not in the data directory, it will automatically copy deadsync.ini, save/, and legacy cache subdirectories into the new data/cache locations. Install-folder songs/ and courses/ are not copied; they remain in place and are still scanned in non-portable mode. The originals are not deleted — you can clean them up manually after verifying everything works.
We welcome contributions of all sizes. Keep changes focused, and open a discussion or draft PR when the direction is uncertain.
- Keep layer boundaries sharp:
engineis reusable infrastructure,gameowns domain rules,screensowns UI, andapp/assetsbridge systems. - Favor idiomatic, direct Rust: small cohesive functions, short names, explicit ownership, and methods or traits only when they reduce complexity.
- Use
Resultfor external failures, panic only on programmer errors, documentunsafe, and test observable behavior rather than implementation details. - Profile before optimizing hot paths. Pre-size and reuse buffers, favor contiguous data, and avoid surprise allocations, I/O, or locks during gameplay.
- Remove dead or replaced code and keep dependencies lean. Run
cargo fmt --check,cargo check,cargo clippy, and relevant tests before submitting.
When reporting a bug, attaching a log file helps the team diagnose the problem quickly.
Open deadsync.ini (created after the first run; see Data Directories) and set the following under [Options]:
[Options]
LogToFile=1
LogLevel=Debug| Setting | Values | Default |
|---|---|---|
LogToFile |
1 (enabled) / 0 (disabled) |
1 |
LogLevel |
Error, Warn, Info, Debug, Trace |
Warn |
Use Debug for most bug reports. Use Trace only if asked—it produces significantly more output.
Launch the game and reproduce the problem. The log is written to deadsync.log in the data directory (see Data Directories above). The previous two runs are kept alongside it as deadsync-<timestamp>.log.
Attach deadsync.log to your GitHub issue or discussion. If the file is large, compress it first (.zip / .gz).
Privacy note: The log does not contain passwords or API keys, but it does include file paths and song/pack names from your system. Review the file before sharing if that is a concern.
DeadSync would not exist without years of work from the StepMania and ITG communities. In particular, we would like to acknowledge:
- StepMania and its contributors for creating the original engine that made all of this possible, including the original StepMania Team and the StepMania 5 developers.
- ITGmania and its developers for shaping the modern ITG experience on dedicated machines.
- Simply Love, its maintainers, and important forks such as zmod, whose work defines much of the current ITG player experience.
And more broadly, everyone who has written themes, noteskins, tools, and simfiles for the community over the years. ❤️
