• v0.8.0 d04c25e6eb

    v0.8.0 Stable

    joe released this 2026-08-18 21:44:10 +01:00 | 0 commits to main since this release

    Every command a chart can use has now been read against the format's own reference and against
    OpenTaiko's source, and the four that were left unimplemented are in. Charts using the
    BEMANI-style scroll modes are drawn the way they were written for the first time, notes can be
    told to appear late and hold still, and they can be sent across the screen in any of eight
    directions.

    The one fault in it is in scoring, and it is a large one: on a branched chart the million was
    being divided between the wrong path, which mis-valued 280 of the 308 branched courses in a
    3578-chart library and, on two of them, made every note worth the entire million.

    Added

    • #HBSCROLL and #BMSCROLL — 35 charts. These come from the BEMANI series and measure a
      note's distance in beats where Taiko measures it in time, so a tempo change slides every
      note already on screen instead of changing the speed of the ones that follow. Supporting them
      meant carrying a second timeline through the parser, in sixteenth notes, alongside the clock.
      #BMSCROLL additionally discards #SCROLL entirely.

      The two models agree completely on a single note — one beat away is the same distance under
      either — and differ only on two, which is worth knowing because the obvious test passes under
      both and proves nothing.

    • #SUDDEN — 12 charts. Notes stay invisible until a set time before they are due, then
      appear already in position and hold still until a second time, and only then travel. Four
      rules came out of the reference and none of them were guessable: an unset value is positive
      infinity rather than zero, so misreading it blanks the chart rather than delaying it; the move
      time is truncated to whole milliseconds where the show time keeps its fraction; a show time
      with no move time hides rolls outright, which the reference's own source calls a bug and
      keeps; and only the horizontal freezes, so a note under a complex #SCROLL drifts vertically
      while standing still.

    • #DIRECTION — 2 charts. Eight directions, and the diagonals travel √2 times as fast
      because both components carry the full speed rather than the speed resolved between them. Its
      vertical is only a suggestion: a complex #SCROLL overrides it, while its horizontal is
      replaced outright.

    • SCOREMODE: and a [scoring] section in config.toml. The header selects one of the
      older arcade schemes — flat 1000 a note, or eleven tiers stepping every ten combo, or five
      tiers stepping at 10, 30, 50 and 100 — and it reaches the score only when Shinuchi is off,
      exactly as in the reference, where ShinuchiMode gates the whole thing and defaults to on.
      So shinuchi = true is the default here too, and 118 charts that set the header are scored
      as they always were until you say otherwise.

    Fixed

    • The million is divided between the Master path, not the Normal one. On any branched chart
      the reference fixes the per-note value at load from listChip_Branch[2] — unbranched notes
      plus the Master path — whatever the player then plays. That is deliberate and is the branch
      system's whole incentive: a run that stays on Normal does not total a million, and reaching
      Master is what pays. Dividing by Normal instead mis-valued 280 of 308 branched courses,
      often by more than double, and on two of them — Taiko Tower 02 Kara-kuchi's Tower and
      Unending Freedom's Easy, where the Normal path holds no missable note at all — it hit the
      zero-notes guard and made every note worth the whole million.

      All five recorded plays still match to the point, because none of those charts distinguishes
      the two rules: SHAPESHIFTER's Normal and Master paths hold the same 1001 notes.

    • A scroll mode set before #START is honoured. That was the required position for these
      commands until the format's 1.2 revision and the reference still reads them there. Two charts
      use it, and both were being recorded as unknown commands and drawn with the wrong model.

    Changed

    • The soul gauge keeps the rate it had. The gauge's fill and the score's denominator are two
      different rules in the reference — the score is fixed at load from the Master path, the gauge
      follows whichever path is being played — so fixing the one deliberately did not move the
      other. The gauge remains normalised over the Normal path, which is identical to the reference
      on every unbranched chart and is listed as open for the 308 that branch.

    Documentation

    • docs/reference-constants.md §2c and §2d — which path the
      million is divided between, and the three scoring schemes with the switch that hides them.
    • docs/format-notes.md §7d — the beat timeline, #SUDDEN, and
      #DIRECTION; and why a complex #SCROLL on a bar line needed no fixing after all, the
      rotation it might have wanted being a TJAPlayer3 bug OpenTaiko does not have.
    • The soul gauge rate should switch with the branch path and does not. Found while settling
      the scoring denominator, and recorded rather than fixed: it needs the gauge to carry three
      rates rather than one, which is a change to the type and not a constant.
    • Two lists of open questions were retired, both having been answered. Step 0.4's six —
      judgement windows, the score formula, gauge rates, roll and balloon scoring, the OFFSET
      sign, and the scroll maths — now point at where each was settled.
    • Two new steps: the screen the arcade draws, and charts that are not TJA. And a note on
      the five pure crates, which are likely to outlive this binary and should be changed with that
      in mind.
    Downloads
  • v0.7.2 8a6fdcb088

    v0.7.2 Stable

    joe released this 2026-08-18 12:21:56 +01:00 | 3 commits to main since this release

    Nothing new. Every command a chart can use was read against the format's own reference and
    against OpenTaiko's source, and it turned up one more fault of the same family as 0.7.1's: a
    section written to run backwards was running forwards instead. Nine courses across six
    charts
    were laid out wrongly, the worst of them by eight seconds.

    Fixed

    • A negative #BPMCHANGE is honoured rather than thrown away. A negative BPM flips the
      sign of the beat duration, which is how a chart lays a section out backwards, and the
      reference puts no check on the value at all. Refusing it would on its own have played those
      sections at the wrong speed — but a negative #MEASURE was already honoured, and charts
      pair the two. A negative bar over a negative BPM is a positive time duration, so taking
      one sign and refusing the other produced the one combination neither is meant to appear in
      and walked the definition cursor the wrong way. Across the 3578-chart corpus this moved
      9 courses in 6 charts by 1.03 to 8.06 seconds, changed no note count, and touched
      nothing else.

      Two of the three affected non-Dan charts check themselves: the fixed course was the only
      one in its file using a negative #BPMCHANGE, and it now ends where its own siblings
      always did. Rainy Tear's Oni ended at 208.62 s against 209.66 s for its Hard, Normal and
      Easy, and now ends at 209.66 s too. Nothing arranged that agreement.

      This one would not have been caught by the end-of-audio check that 0.7.1's work
      recommended: all nine last notes fall inside their own audio before and after. Displacement
      inside a song is the harder kind to see, and only the reference finds it.

    • #BPMCHANGE 0 stays rejected, being the one value with no meaning. The reference would
      divide by it, the format reference marks it unspecified and records that it crashes some
      simulators, and no chart in the corpus writes one.

    Documentation

    • Every command in the corpus is now accounted for, in
      docs/format-notes.md §7c — forty of them, each either implemented,
      confirmed correct against a named line of the reference, or recorded as a deliberate
      divergence with the number of charts it costs. Three are known to be wrong and are written
      down as such rather than left to be rediscovered: #HBSCROLL and #BMSCROLL (35 charts)
      select a beat-based position model we do not draw with, #SUDDEN (12 charts) is
      unimplemented, and a complex #SCROLL should rotate bar lines where we move them
      vertically.
    • #NMSCROLL turns out to be the default mode, so ignoring it was already right and the
      19 charts using it were never wrong — one of several assumptions this pass was able to
      retire rather than reopen.
    • A miscount was corrected. The note-character table claimed A and B never occur in
      the corpus; they occur 78 and 109 times, and did when it was written. Nothing behaved
      wrongly, both having been aliased to 3 and 4 regardless, so the fault was only ever in
      the writing — which is the argument for checking the notes against the reference and not
      only the code.
    Downloads
  • v0.7.1 9a59742125

    v0.7.1 Stable

    joe released this 2026-08-18 02:56:13 +01:00 | 5 commits to main since this release

    Nothing new. Charts that branch were being read wrongly in six separate ways, and the worst
    of them left 181 charts playing seconds out of step with their own music — 90 of those
    ending after their own audio had stopped, with notes nobody could ever reach. This release
    only puts notes back where they belong.

    The faults were found by walking the format against OpenTaiko's source rather than reasoning
    about it, and then settled by playing SHAPESHIFTER — the chart that prompted the work —
    beside OpenTaiko playing the same file. Its gimmicks now land at the same moments in both.

    Fixed

    • 181 charts were playing seconds out of step with their own music. A #MEASURE or
      #BARLINEOFF on the line before a #BRANCHSTART was being read as an empty bar of rest,
      which pushed the branched section — and, since the song carries on from where a branch
      ends, everything after it — most of a measure late. Commands are not a measure; only note
      data makes one. Across the 3578-chart corpus this moved 216 courses by a median of 6.3
      seconds
      and put 90 of them back inside their own audio: SHAPESHIFTER's Oni was
      ending 5.8 s after the song had finished, with notes that could never be played.
    • Branches are decided at the right moment. The determining point is the last bar line
      before the branch, so the lead is however long that measure was — not a measure computed
      from the signature in force. SHAPESHIFTER's fake-outs lead by half a beat and now do; they
      led by 3.5 beats, so the swap the chart exists for happened off screen. Roll conditions
      wait for the roll to end, a hidden bar line still decides a branch, and a chart opening on
      one is judged four beats before it starts.
    • Branch points are ordered by when they are decided, not by where their section starts
      — two different times, which a negative #MEASURE can put in different orders. Ties keep
      the order they were written in, so a chart can bury an unwanted branch change under a
      wanted one, as the reference's own changelog describes.
    • A branch group ends where its last-defined path left off, not where the longest one
      did — the same answer on an ordinary chart, and the wrong one whenever a path rewinds
      through negative measures. This is what stopped SHAPESHIFTER's central trick working:
      its two branch changes are folded onto one instant so that the unwanted one is hidden
      under the wanted one, and we were missing that instant by three bars and showing both.
      You now see one "level up" at 75 s where there were two changes 2.45 s apart.
    • A branched chart names its path from the first frame, reading "Normal" until something
      changes it, rather than showing nothing until the first branch. HIDDENBRANCH is now read
      and is the only thing that holds the name back.
    Downloads
  • v0.7.0 7beb355ec5

    v0.7.0 Stable

    joe released this 2026-08-17 19:28:47 +01:00 | 11 commits to main since this release

    The drum is the whole interface now: it works the menus, the screens say so, and the
    playfield answers every hit.

    Added

    • Every hit is answered. A note bursts at the judge ring and flies off to the soul
      gauge on a bowed arc, which is what makes a fast stream read as a stream — the last dozen
      hits are still on screen. The gauge flashes when a note lands on it, 560 ms after it was
      struck, rather than when it was hit. Every timing is OpenTaiko's, written up in
      docs/reference-constants.md §7; reading them rather than
      guessing turned up three things nobody would invent, including that a BAD is answered
      with nothing at all
      .
    • The combo bounces on the reference's own thirteen-step curve, peaking 18% larger four
      frames in, and changes colour at 50, 100 and 300. The judgement eases into place over
      410 ms and fades over the last 50.
    • A big note taken with both hands gets a second, larger burst — the distinction has
      been scored since 0.4.0 and shown nowhere until now.
    • Go-go time arrives rather than merely being tinted: the lane brightens and a spray of
      sparks is thrown up from six points across the bottom.
    • Rolls and balloons answer too. A roll shows a framed count that lingers 2.44 s after
      the last hit rather than blinking with each one; a balloon counts down and deflates in
      five stages as its target approaches.
    • The drum works the menus, as it does on the cabinet: the rim moves — D up, K
      down — and either don key, F or J, chooses. That is the whole interface for
      anyone playing on a real drum, where those four inputs are all there is. D no longer
      moves the cursor right, since one key cannot both move and adjust on a screen that reads
      them together.
    • A way out you can see. Song select repeats a Return entry through every category
      — the arcade's floor(n / 7) + 1 of them, so a way out is never more than seven presses
      away — and the difficulty screen offers Back first, before the difficulties. Bound
      keys were not enough: on a real drum there is no Escape key.

    Fixed

    • A song no longer loops when its audio runs out before its chart does. "R"ast GaMES!! played itself twice, and the second time — every note already resolved — all
      that appeared were the drumrolls. raylib loops a music stream by default and the clock is
      the audio's own position, so the wrap took chart time back to zero with it. A song now
      ends when either its chart or its audio does.
    • A one-pixel line of undimmed bar along the bottom of the song wheel, where the fade
      stopped a row short of the screen.
    • A category explanation split across lines no longer reads community- made.

    Changed

    • Autoplay drums rolls, at the 16.6 hits a second the reference assumes when budgeting
      a roll's share of the score. It never touched them before, which left the only feedback a
      roll has impossible to see without a player. One consequence: --auto can now score over
      1,000,000 on a roll-heavy chart, which is what happens to anyone rolling faster than the
      budget assumes.

    Documentation

    • Branching, as the format actually defines it — the determining point is the last bar
      line before #BRANCHSTART, branch changes at the same instant resolve by definition
      order, and negative #MEASURE overlays rather than skips. Three sources agree:
      the charter of SHAPESHIFTER with his own diagrams,
      IepIweidieng's TJA reference,
      and OpenTaiko's source. We have it wrong, in a way this release does not yet fix:
      see step 17a in implementation-plan.md.
    • Step 11's gate is reopened. SHAPESHIFTER was signed off as matching the reference and
      does not; the score check that appeared to confirm it is arithmetic on judgement counts
      and says nothing about where a note is drawn.
    Downloads
  • v0.6.0 08fcf54bfd

    v0.6.0 Stable

    joe released this 2026-08-17 10:48:08 +01:00 | 22 commits to main since this release

    Song select was a file browser that happened to hold songs. It is now a wheel, and so is
    every menu that reaches it.

    Added

    • Song select is a wheel of coloured bars, turning past a fixed middle with the
      selection opened out — its jacket, credits and difficulty levels inside the bar rather
      than in a panel off to one side. The neighbours above and below stay visible, so moving
      reads as the wheel turning rather than a page redrawing.
    • Categories bring their own colours and descriptions. box.def has always carried
      them — 50 of this corpus's 51 categories set BOXCOLOR and 41 set the explanation
      lines — and none of it had ever been drawn. Songs wear the colour of the box they are in,
      which turns a library into a set of coloured places rather than a page of words.
    • The main menu and the settings screens are the same wheel. Each settings screen has a
      colour and its rows wear it, so opening Audio keeps the colour of the bar that opened it.
      Values are drawn on every bar, not only the selected one, so a screen of settings can
      still be read at a glance. Bindings stays a list on purpose: it is a table to be read, and
      a wheel would show one binding and hide the other ten.
    • Songs are auditioned while you browse. Resting on one for a third of a second plays it
      from its DEMOSTART — the part the charter chose — fading in, and returning there after
      25 seconds rather than wandering into the rest of the song. Scrolling past a hundred songs
      starts none of them.
    • Holding a direction scrolls, accelerating after a second so a 500-song library can be
      crossed without pressing anything 500 times.

    Fixed

    • A quarter of the jackets never appeared. raylib builds without JPEG support unless
      asked for it, and 94 of this library's 398 covers are JPEGs — they failed to load and drew
      as "no art" with nothing said about why. All 398 now load.
    • Loading a jacket no longer stutters the browser. Decoding one takes 8.6 ms at the
      median and 164 ms at worst — ten frames — so with a load on every step, scrolling
      visibly hitched. Decoding and resizing now happen on their own thread; only the upload to
      the GPU stays on the main one, and that is 0.8 ms at its worst. The selection is fetched
      first and then the two bars either side, so stepping to a neighbour usually finds it
      already there.
    • Jackets are held at 512 px rather than 1024 — one is drawn at about 475 px on a 4K screen —
      which takes a full cache from 81 MB to 25 MB, and the cache now evicts oldest-first
      instead of emptying itself when it fills.
    • A category explanation hard-wrapped mid-word no longer reads community- made.
    Downloads
  • v0.5.1 4ca9a1b246

    v0.5.1 Stable

    joe released this 2026-08-16 14:55:06 +01:00 | 37 commits to main since this release

    No change to the game itself. This release adds a Steam Deck build, confirmed running
    on the device.

    Added

    • A binary for the Steam Deck, and for any distribution too old for the main one. Built
      here, the binary asks for glibc 2.43 and the Deck has 2.41 — so it does not start there
      at all, failing with a message about a version not found in a file that plainly exists.
      The gap was two symbols: acosf and atan2f, from raylib's maths.
    • scripts/build-deck.sh builds inside Valve's Steam Runtime "sniper" SDK, targeting glibc
      2.31, and refuses to finish if the result asks for more than the Deck can give. Steam is
      not needed to run the result: an old glibc is a link-time floor, not a runtime
      dependency, so the binary runs on the Deck whether Steam launches it or not.
    • Both binaries now go out with every release.

    Documentation

    • docs/steam-deck-build.md — how the container is put together,
      what was rejected and why, and how to check a binary yourself. Including the trap:
      run-in-sniper will happily run a binary the Deck cannot, because pressure-vessel
      imports the host's newer libc into the container.
    • Releasing is written down, with the one piece of upkeep it needs: when SteamOS has a
      major update, check the Deck's glibc and update DECK_GLIBC.
    Downloads
  • v0.5.0 88748a071a

    v0.5.0 Stable

    joe released this 2026-08-16 14:11:52 +01:00 | 41 commits to main since this release

    0.5.0 — 2026-08-16

    Charts that play tricks with the format now play correctly. All of this came from one
    song — SHAPESHIFTER — which turned out to be wrong in four separate ways.

    Added

    • Branch switching. Charts change between the normal, expert and master paths mid-song,
      and the change is visible: the notes already on screen are replaced. Every path is now
      kept and the chart plays whichever it chooses, decided one bar ahead as the reference
      does. Charts use this as an effect rather than as difficulty — SHAPESHIFTER asks for 101%
      accuracy to leave the normal path, which cannot happen, and 0% to reach master, which
      always does — so playing only the normal path showed the wrong notes rather than a
      reduced chart. #SECTION and #LEVELHOLD come with it.
    • The path is named on screen, at a fixed place in the lane, with the branched section
      tinted from where it begins.
    • #JPOSSCROLL moves the judge position. Charts pair it with a reversed #SCROLL to
      mirror the lane, so honouring the scroll while ignoring the move sent notes in backwards
      past a hit zone that never moved.
    • Complex #SCROLL values. The real part is the horizontal speed as before; the
      imaginary part moves the note vertically, so 0+1i sends notes straight up.

    Fixed

    • Negative #MEASURE walks the time cursor backwards, which charts use deliberately —
      SHAPESHIFTER rewinds 35 beats, and elsewhere it appears as -1/64 nudges. Treating them
      as nonsense left the cursor moving forwards instead, running one difficulty ten seconds
      past the other four of the same song.
    • The button that opens calibration is no longer counted as the first tap. A gamepad's
      A button is both Confirm and don, so calibration was measuring a keypress made on a
      different screen.
    • Balloon counts are per path, so a balloon on a path not being played no longer consumes a
      count meant for the path that is.
    • A balloon the chart never sized needs five hits rather than being unpoppable.

    Changed

    • main.rs is 662 lines rather than 910, with startup and chart loading in their own
      modules and the audio, video and bindings screens sharing one implementation.
    • New autoplay gate: every chart in a library is played perfectly and anything that says
      it cannot — a note that will not resolve, or one a perfect player still missed — is a
      failure. 1907 courses and 1.3 million notes pass. It was written for a bug it now catches,
      where a branched chart could hang the game at 100% CPU.
    Downloads
  • v0.4.0 095ee7d315

    v0.4.0 Stable

    joe released this 2026-08-16 00:10:37 +01:00 | 58 commits to main since this release

    Added

    • Japanese titles render. Song titles were drawn in raylib's built-in font, which has no
      Japanese, so a large part of the library appeared as rows of ?. The game now picks fonts
      from what is installed: the desktop's own sans for the alphabet, plus whichever installed
      font covers the most of your particular song titles, each character drawn by the first font
      that has it. No font is shipped, and the startup line names what was chosen.
    • Left and right are told apart on the drum. Hitting the left side colours the left half of
      the target circle and the right side the right half; a rim hit colours that side's rim rather
      than the face. Autoplay alternates hands, so the feedback looks the way real play does.
    • Big notes reward both hands. A big note struck with one hand scores as a normal note;
      landing both within 50 ms doubles it, as on the arcade cabinet. Previously either hand alone
      took the full doubled value, which made the two-handed notes pointless.
    • --screen select opens the song list directly, alongside the existing settings screens.

    Fixed

    • Colour markup in titles (<c.#cd7f32>玄人</c>, used by the dan ranks) was drawn literally,
      tags and all. It is now stripped to the plain title. The colour is not reproduced.
    • Text is filtered when scaled. Glyphs are rasterised once at 48px and drawn between 13 and 34,
      which with raylib's default point sampling dropped pixels at small sizes.
    Downloads
  • v0.3.1 b509169361

    v0.3.1 Stable

    joe released this 2026-08-15 20:30:40 +01:00 | 62 commits to main since this release

    No changes to the game itself: release tooling and documentation only.

    Fixed

    • scripts/release.sh left the debug binary behind. The version is baked in at compile
      time and the script only built --release, so ./target/debug/taiko kept reporting the
      previous version after a release. It now refreshes the debug build too.
    • Cargo.lock was not synced to the manifest before the clean-tree check, so a version bump
      left the lock naming the previous version at the tag. The v0.3.0 tag has this; nothing
      is broken by it, since cargo regenerates the lock, but the tag did not describe its own
      build. The lock is now synced first, and a stale one blocks the release with a note.
    • --dry pushed the branch to the remote — precisely the side effect a dry run promises not
      to have. It also refused to run against an already-released version, though rebuilding one
      to inspect what went out is a reasonable thing to want.

    Documentation

    • How to get songs and hit sounds, with the expected directory tree. The soundtrack
      repository can be cloned and played directly with no OpenTaiko install. The drum samples
      are in neither OpenTaiko repository — they ship in the release archive — which is the
      step anyone following the repos alone would get stuck on. Skins are not used at all.
    Downloads
  • v0.3.0 cd38b55eb1

    v0.3.0 Stable

    joe released this 2026-08-15 20:11:31 +01:00 | 67 commits to main since this release

    Added

    • A main menu — Play, Settings, Quit — and a settings menu behind it: calibration,
      audio, video, bindings and an input test.
    • Audio and video settings screens. Latency offset, volumes and the effects bypass;
      fullscreen, V-Sync and the frame limit. Fullscreen and the frame limit apply immediately;
      V-Sync says on the row that it needs a restart rather than appearing to do nothing.
    • A bindings screen listing every action and what is bound to it. Read-only for now —
      editing it needs a mode, not a rewrite, since capture already exists.
    • The input test now returns to settings after 10 seconds with nothing pressed, and says so
      on screen. It consumes every input while open, so a player diagnosing a broken Back
      binding could otherwise have had no way out.

    Fixed

    • Opening a screen immediately acted on the keypress that opened it — choosing Settings
      landed straight in Calibration. raylib refreshes input at the end of a frame, and
      switching screens skipped the draw, so the same press was still down when the new screen
      read it. Every transition now absorbs the input that caused it.

    • Leaving a song shared a button with the drum. The arcade layout puts every face
      button on the drum while Back conventionally sits on one of them, so hitting the drum
      could quit the song. Leaving a song is now its own action, defaulting to Start/Select on
      a pad, and conflicting bindings are reported at startup.

    • Controllers were not detected. The game listened to gamepad index 0, which is rarely
      the controller: an 8BitDo Ultimate 2C presents its keyboard interface at index 0 and the
      actual joystick at index 1, and the indices shift as devices come and go. input.gamepad
      now defaults to "all" and listens to every device, so a controller works with no
      configuration. "none", an index, or part of a device name still pin it.

    • Configs written by 0.2.0 still load: the old input.gamepad_index is accepted, ignored,
      and reported once, rather than being rejected and silently taking the player's calibration
      with it.

    Changed

    • Bindings are now actions, and every control is bindable. Previously drum hits came
      from config while menu keys were hardcoded in Rust, and "back" during a song was a
      keyboard-only check in the main loop — so there was no way to leave a song on a gamepad,
      and nothing a settings menu could have enumerated. Now [input.bindings] maps ten actions
      to any number of inputs: keys, pad buttons (pad:NAME, or pad2:NAME for one device),
      and pad axes (axis:LEFT_Y+), with back bound on both keyboard and pad everywhere.
    • Drum actions carry a side (don_left / don_right), which nothing uses yet but big notes
      needing both hands eventually will.
    • Configs from earlier versions are migrated, not discarded: custom don/ka keys move
      into the new bindings and the change is reported.

    Added

    • --save-config writes the config with every binding spelled out.
    • --test-input, which lists the devices being listened to and names every key and button as
      it arrives. Written because "my controller does nothing" is almost always the wrong device
      being polled, and that is invisible without it.
    Downloads