Converts between standard MIDI files, Piano Tiles 2-style song notation JSON, and USong binary files (v0.1.1), entirely in your browser. Format reference:
pianotiles2-community.github.io/wiki/song.html
MIDI → JSON
JSON → MIDI
Edit JSON
USong → MIDI
🧬 MIDI Merger
🎮 Test JSON
🏆 Challenge Maker
Load MIDI
Drop a .mid file here, or click to browse
Parsing happens locally in your browser
Settings
Smallest tile ?
Chord timing ?
Measure length ?
Song parts ?
Transpose ?
?
Double tile length ?
?
?
Per-track roles are set next to each track below: Auto uses the chord timing field above as normal; 🎵 Melody keeps that track's notes as separate individual tiles no matter how close together they start (so a fast melody line never gets absorbed into a held chord's tile); 🎹 Chords always groups that track's simultaneous notes into one tile even if chord timing above is set low. This is the fix for "melody notes end up inside the chord's long tile" when melody and chords come from different MIDI tracks.
Tracks (each becomes one scores voice)
Per-part tempo — e.g. part 1 = 100 bpm, part 2 = 105, part 3 = 110...
baseBpm ?
?
?
Chance per group (%)
Output preview
Extract notes only ?
Uses the same settings above (min tile, chord timing, transpose, merge/DRUNK/Smart Sync, track selection) but outputs plain notation text instead of a JSON object — nothing gets wrapped into musics/scores.
?
?
5<> is the only tile type with confirmed syntax in the wiki ("double tile"). It merges multiple notes/chords across a paired lane at once — 2, 4, 8, even 16 at a time all work in-game.
3<> 6<> 7<> 8<> 9<> 10<> are named and described, but the wiki gives no worked
example or note-count limit for any of them. Everything else here (pitch names, chord grouping, quantized durations, grid
self-correction, measure barlines, time-signature detection) is implemented from confirmed spec.
🍺 DRUNK Mode's non-5<> tile types use your own chosen min/max note counts because the game doesn't document any —
there's no "correct" number to check against. Start with small ranges (2–6) and test in-game before trusting a file with these enabled.
Load JSON
Drop a song JSON file here, or click to browse
Parsing happens locally in your browser
Settings
MIDI division?
Fallback BPM?
Note velocity?
Transpose ?
Score tracks
Quantization — snaps note timing to a rhythmic grid, GarageBand-style
Type ?
Grid division ?
Swing style ?
?
Reconstructs pitch and rhythm exactly from the notation letters, including unpacking 5<> double tiles.
mute tokens produce a timed rest. Ignores baseBpm/baseBeats scroll-speed scaling
(uses each part's bpm directly) since the in-game speed formula isn't published.
Create a blank JSON
Generates an empty, structurally-valid song — add notes yourself for a custom challenge.
Parts
Score tracks per part
baseBpm
bpm / baseBeats per part
Load JSON to edit
Drop a song JSON file here, or click to browse
Edit directly as text, or use the token editor below
Raw text
Token editor
Click a token to select it, edit its text directly, or use the buttons below. Changes here re-write the raw text above.
Transpose ?
Semitones ?
Out-of-range notes ?
Quantize timing — same grid engine as the JSON → MIDI tab, applied directly to the notation
Rewrites the rest/note letters themselves so the notation's own timing snaps to a grid — useful when you want the quantized version saved back as JSON, not just baked into an exported MIDI.
Type ?
Grid division ?
Swing style ?
?
Duplicate notes ?
Apply to ?
Merge fast notes into grouped tiles ?
Duration letter to merge ?
Tile type ?
Notes per group ?
Convert tile types ?
Convert from
Convert to
Only convert tiles whose notes use these duration letters ?
Merge consecutive tiles into one ?
Merge this tile type
How many in a row ?
Into this tile type
Only merge tiles whose notes use these duration letters ?
🍺 Make this JSON DRUNK
Randomizes tile groupings directly on this JSON's existing notes — no MIDI needed, works even if you've lost the original file.
Chance per group (%)
🎲 Random note injection ?
Independent from DRUNK-ify above — this only adds extra pitches to notes, it never restructures tiles.
Chance per note (%) ?
Min extra notes
Max extra notes
Apply to
Pitch of extra notes ?
± semitones
Auto-fix strips trailing commas, balances unmatched brackets, trims garbage before/after the JSON body, and converts
stray single quotes — a best-effort heuristic, not a guarantee, especially for malformed note-notation strings.
Load USong
Drop a .usong file here, or click to browse
Format v0.1.1 · parsed entirely in your browser, nothing is uploaded
Only format version 1 (v0.1.1) is supported, since v0.1's header layout isn't documented. The song format
doesn't fully define how tempo evolves under linear and classic PT2 acceleration, so this
converter makes explicit choices — tap the ? below for details. Output MIDI always uses a fixed reference
tempo of 120 BPM at 960 ticks/quarter; USong's own tempo curve is baked directly into note timing instead, so
playback stays accurate regardless of what a MIDI player assumes about tempo.
Tempo timing assumptions?
— which mode is active is reported in the parse log for every file you load.
🧬 MIDI Merger ?
Load two or more MIDI files, pick which tracks from each to include, choose Chain or Layer mode, and download one merged .mid. Handy for stitching together the parts of a large song, or combining separately-composed instrument tracks into one file.
Drop MIDI files here, or click to browse
Add as many as you like — each one appears below with its own track list
No files loaded yet.
Merge settings
Mode ?
Output ticks / beat ?
Gap between files (beats) ?
Load JSON to test
Loads a Piano Tiles 2 song JSON straight into the real Piano Tiles Zero game engine — no download, no copy-paste round trip needed. The JSON is parsed entirely by the game's own parser, not this site's.
–
Notes
–
Parts
–
Est. duration
–
Pitch range
🕘 Recently played ?
🎮 Real game ?
Must be served over http(s) (not opened as a bare local file://) for the iframe and Tailwind CDN to load correctly — run it from a local web server if it looks blank.
Loading game…
▶ Playback
🖥 Display
❤ Survival
🏆 Challenge
⌨ Keys & Sound
Custom backgrounds ?
Level 1 background
Level 2 background
Level 3 background
Low-perf fallback
1. Load source songs ?
Every file is checked for structural problems (bad JSON, missing parts, unrecognized notes/pitches) as soon as it's loaded — broken files are marked and can't be selected, so nothing unplayable can make it into the final challenge.
No songs loaded yet — add at least 3 JSON files to build a challenge.
2. Challenge structure
Number of parts in the challenge ?
?
Select songs above to continue — 0/3 selected.
3. Part slots ?
4. Generate
Before combining anything, every resolved part is re-validated (pitch names, token shapes) and every part's scores array is padded/rebuilt to the same track count, with fresh sequential id/instruments/alternatives fields — the most common causes of an in-game crash when songs from different sources get mixed together.