One HTML File and a Console: How MusicLink Got Tuned

MusicLink was prototyped as a single HTML file so its rules could be changed mid-game from the browser console. The dials that mattered, and what each one fixed.

One HTML File and a Console: How MusicLink Got Tuned

Before writing any Swift for MusicLink, I built the whole game as one HTML file. No framework, and no build step beyond inlining the catalog. The point was to find out whether matching albums by musician is fun, and to find out cheaply.

Every rule is a dial

All the tunable numbers live in one object, ML.cfg, reachable from the browser console:

ML.cfg.spawnStart = 2200   // ms between drops in round 1
// then hit Play again

Change a number, play again, feel the difference. ML.state() returns the live game state when something looks wrong. A game about feel needs a short loop between "what if" and "oh, that's worse", and this one is a few seconds long.

The dials that mattered

Drop speed. Round one drops a cover every three seconds. Each round is 12% faster, with a floor of one second. Round N needs 8 + 2(N−1) links to clear.

Partner bias. A purely random drop from a thousand albums almost never links to what's on the board. So 70% of the time, a drop is deliberately a partner of something already there. This is the single most important number in the game. Without it you stare at twelve unrelated covers.

The run window. After a match, the cleared group stays open for six seconds, and a cover that links to any member clears on its own at a rising multiplier. For a while after a run, drops lean toward partners of the group you just cleared, so a run can keep feeding itself.

Misses. A wrong pair costs no points. Taking points away would punish the fun part, which is guessing that two records might share a bass player. Instead, the next cover lands 350 milliseconds later. You pay in pressure.

Wild cards. Two links in a row with no miss queues a wild card for the next drop. It links to anything for 150, and at most two can be waiting.

Selecting a cover names it; a run in progress

Details that turned out to be load-bearing

  • Tap a cover and a bubble names the album, artist and year, because not every cover is recognisable at thumbnail size.
  • The grid picks 3×5 over 4×6 when the larger grid would make covers smaller than 96 pixels, the point where they get hard to read.
  • If nothing has been tapped for nine seconds, the Hint button starts to glow.
  • Each new round clears the board and seeds a fresh one, with a good chance that an opening pair has a third cover that links to it.

The ending teaches you the game

When the stack overflows, the game walks through up to eight links that were on the board and went unclaimed, deep cuts first. Losing becomes the tutorial. You see that those two covers shared a producer and you go straight back in to look for it.

The walkthrough of missed links, then the score

A robot plays it too

A Playwright script loads the built page at phone size, works the catalog picker, plays a few links through the game's own tap logic, forces a game over and walks the reveal, taking screenshots along the way. It catches the build breaking, which with a single 13 MB HTML file full of inlined album art is more likely than it sounds.

The same rules are now being carried over to the iPhone build. The numbers above are the ones it starts with.

Play MusicLink, and open the console if you want to cheat.