A combo trainer built for Wuthering Waves. A small local web UI + Python backend listens to your keyboard/mouse and tracks whether you performed a rotation correctly, including wait and hold timing steps.
Every combo is tied to a team of three Resonators: the timeline shows each character's portrait on swaps (1/2/3) and their own Basic Attack, Resonance Skill (e), Echo (q) and Liberation (r) icons. A fresh install comes with WuWa default combo enders (swaps, e, r, jump) and auto-transcribe keys.
This combo tracker is meant to track real time, timing data. not in game timing data. For example, shorekeeper's Liberation is 2.9s in real time, but has a in game freeze time of 2.83s. Therefore it has a In game time of only 0.07s but the combo tracker will display as the full 2.9s
- Practice combos: see live status + a step timeline.
- One-click characters: in Team & Characters, type a name (e.g.
Phrolova) and press Add from wuthering.gg to download their portrait and skill icons. Refresh all icons re-downloads icons for every saved character. Needs an internet connection. - Move names: each step is labeled with the move it performs, like "Zani Basic 2", "Phoebe Intro" or "Tune Break", using your team slots. A leading
fis the fight-start prompt; any laterfis Tune Break. Toggle with Move names above the timeline. - Wait + hold steps:
wait= minimum delay gate (pressing later is OK).hold= finger commitment (must hold long enough).
- Combo enders: define which “wrong” inputs should drop the combo.
- Stats: success/fail, best time, hardest steps, fail reasons.
- Difficulty scoring (simple + tunable):
- Practical APM (uses your expected execution time)
- Theoretical max APM (uses fastest-possible time)
- Difficulty out of 10 (keys + timing + simple timing-variation rule)
brave_7MveomYeIv.mp4
If you downloaded a packaged release, keep the release folder intact and run
ComboTracker.exe from inside it. To run from source, use Python as described
below.
- Python 3.10+ recommended
Install dependencies:
cd ComboTracker
python -m pip install -r requirements.txtcd ComboTracker
npm run serve(npm run serve just runs python ui_server.py, which also works without Node.)
Then open the UI:
http://localhost:8737
Notes:
- The backend also runs a WebSocket server at
ws://localhost:8765. - The app listens to global keyboard/mouse via
pynput(you may need accessibility permissions on some OSes).
You can package ComboTracker so others can run it without installing Python.
- Install the requirements, which include PyInstaller (once):
python -m pip install -r requirements.txt
- From the project root, build:
python -m PyInstaller --noconfirm --clean ComboTracker.spec
- Release artifact:
dist/ComboTracker/. Keep the entire folder together and launchdist/ComboTracker/ComboTracker.exe. Windows will prompt for Administrator approval once per launch; this matches elevated games so global input capture works in-game. - Open
http://localhost:8737in your browser. A console window stays open with the server URL (close it or press Ctrl+C to stop). combos.json(saved combos and settings) is written next to the .exe. Keep it in the release folder if you want saved data to move with the app; otherwise a newcombos.jsonis created on first run. Updating the app never touches it: the source download shipscombos.example.json, which only seedscombos.jsonthe first time.
Without a local build: every push runs the CI workflow on GitHub Actions, which runs the tests and builds the Windows release. Download it from the workflow run's Artifacts section (ComboTracker-windows).
Publishing a release: zip and ship the complete dist/ComboTracker/ folder. The one-folder, non-UPX layout avoids the self-extracting executable pattern that commonly triggers antivirus heuristics. Build on a normal (non-elevated) terminal; PyInstaller may warn if you run it from an elevated shell. Code-signing the executable is recommended for public releases.
You can show the Combo Steps timeline in OBS as a separate overlay (e.g. for streaming or recording).
Timeline.1.mp4
- In the main UI, open the Combo Steps section and click Copy Overlay URL.
- In OBS, add a Browser Source, paste the URL (e.g.
http://localhost:8737/?view=timeline), and set the width/height you want. The overlay stays in sync with the app via WebSocket, and reconnects on its own if you restart ComboTracker. - Open in new window is also available if you prefer OBS Window Capture instead of Browser Source.
Browser Source vs your browser: The OBS Browser Source is a separate embedded browser. Toggles (Auto scroll, Images, Show fail count) are per-instance: use Interact on the Browser Source in OBS (right‑click the source → Interact) to open a window where you can click those controls for the overlay. Timeline content and progress sync for all clients; only the toggle state is local to each instance.
Wide layout: In timeline-only view (?view=timeline), the section stretches to fill the width of the Browser Source, so you can set a wide source in OBS and use the space.
For a walkthrough of common OBS Browser Source gotchas (separate instance, interact window, scrolling, sizing), see: OBS Browser Source demo (video, ~2:25).
you can set custom CSS to zoom in
body {zoom : 150%;}
A built-in, NohBoard-style key display for streams and recordings: WASD, Shift, Space, 1/2/3, Q/E/R/F and both mouse buttons light up as you press them. The swap keys show your selected team's character names and portraits, and macro playback shows up too.
- Open it from Settings > Stream overlays, or add
http://localhost:8737/keys.htmlas an OBS Browser Source. The background is transparent. - It reconnects on its own if you restart ComboTracker.
- Only these keys are ever sent to the page, so other typing stays private.
The Characters page (in the sidebar, or http://localhost:8737/characters.html) shows what each Wuthering Waves character's buttons do. It covers inputs only: no damage numbers.
- Moves: the Basic Attack chain (A1, A2, ...), what holding LMB does, which hit LMB lands on after the Intro, Skill, Liberation, a Heavy, a Dodge Counter or a Mid-air Attack, and each move with its key. The timeline's move names use the same data.
- Timings: each ability's frame data from WuwaLAB, laid out like WuwaLAB's Abilities tab: hits, total frames, the earliest cancel frame, how long until you can swap, time stop, motion stop, cooldown and the frame each hit lands on. Switch between frames and seconds (60 fps), and filter by name.
- Team rotations: the community's team setups for that character with author and video, from AntoCrasher's rotation compilation. Combo opens the move-by-move rotation transcript, converts it to tracker inputs (swaps use the team's slot order; no timings), and Save as combo adds it to your combos with the video attached.
The move data ships with the app in static/data/ww_characters.json. It was drafted from the encore.moe API and checked by hand for the characters marked "Checked by hand"; edit that file to fix or add details. Download raw data saves encore.moe's unedited files to data/encore_raw/ without changing anything shown; python tools/ww_build_moves.py then drafts any new characters from them (hand-checked entries are left alone). The timings ship in static/data/ww_timings.json; to update them, scrape WuwaLAB's ability pages into one JSON file and run python tools/ww_import_timings.py that-file.json (damage columns are dropped). Rotations are downloaded when you open them and cached in ww_library_cache/.
Detailed docs live in documentation.md:
- Combo format:
documentation.md#combo-format - Combo enders:
documentation.md#combo-enders - Difficulty + APM:
documentation.md#difficulty--apm - Troubleshooting:
documentation.md#troubleshooting - Architecture / module map:
documentation.md#architecture
Combos and stats are stored locally in combos.json.
If keystrokes are not registering when in game, launch the code in elevated command prompt, with administrator.

