← Back to the gamebug or idea?Terrapin Trail//Code BibleIIhow the game works, archive to van
Reel 17 · 6 min read

How the game talks to the page

How the game gets its tour, plays music, and hands your score to the leaderboard without knowing about any of it.

The game itself is still one file. Around it is a thin page that hands the game its tour and rules, plays music, and takes your final score to the leaderboard. The two talk through a few clearly defined messages.

Handing the game its tour

When you press play, the server prepares everything the game needs (the tour, the rules, a run ticket, the luck) and puts it inside the page before the game starts:

●runner.py : 292–318trace ↗
292    def play() -> BaseResponse:
293        """The game. With ?tag= it plays that shared tour under the chosen rules and
294        hands out a run ticket so the score can go on the tour's public board."""
295        base = request.args.get("tag")
296        boot: dict = {"donate": DONATE_URL}
297        if base:
298            try:
299                info, tour = build_tagged_tour(base)
300            except archive.ArchiveError:
301                return redirect("/")
302            values = dna.decode(request.args.get("dna") or "CLASSIC")
303            code = dna.encode(values)
304            luck = request.args.get("luck")
305            try:
306                luck_n = int(luck) if luck else None
307            except ValueError:
308                luck_n = None
309            base = tour["tag"]
310            run = trust.start_run(store, base, code, len(tour["stops"]), 1, luck_n)
311            if code not in dna.PRESETS and store.get("ruleset:" + code):
312                store.zincr("rulesets:plays", code)
313            boot.update({
314                "tag": base, "fullTag": tags.full_tag(base, code), "dna": code, "rules": dna.label(code),
315                "values": values, "tour": tour, "run": run["run"], "seed": run["seed"],
316                "band": info["band"], "year": info["year"],
317            })
318        return _page("play.html", boot)
Look up the tour by its tag, decode the rules, issue the run ticket, and hand all of it to the page.

Messages out of the game

The game doesn't know about leaderboards or playlists. It just announces two moments: "a show was taped" and "the run ended". The page listens and acts:

●assets/web/play.js : 82–91trace ↗
82  window.addEventListener("tt:taped", function (ev) {
83    var t = ev.detail || {};
84    if (!t.identifier) return;
85    try {
86      var tapes = JSON.parse(localStorage.getItem("tt_tapes") || "[]").filter(function (x) { return x.identifier !== t.identifier; });
87      t.tag = B.fullTag || null; t.at = Date.now();
88      tapes.unshift(t);
89      localStorage.setItem("tt_tapes", JSON.stringify(tapes.slice(0, 300)));
90    } catch (e) {}
91  });
When a show is taped, add it to your saved tapes. That's the 'My tapes' playlist in the browser.

Concept: "event". An event is a program announcing 'this just happened' without knowing who's listening. Other code can listen and react. It keeps parts of a program independent: the game never has to change when the page adds a new feature.

The van stereo

The page asks the game where the van is, then plays that night's real recording:

●assets/web/play.js : 25–37trace ↗
25  var TTMusic = {
26    play: function (identifier) { return player ? player.play(identifier) : Promise.resolve(false); },
27  };
28  window.TTMusic = TTMusic;
29  function renderStereo(p) {
30    var np = $("tt-np"); if (!np) return;
31    $("tt-pause").innerHTML = p.playing ? "⏸" : "▶";
32    if (p.blocked) {
33      np.innerHTML = '<button type="button" id="tt-resume">&#9654; Keep listening</button> ' + esc(p.track ? p.track.title : "");
34      $("tt-resume").onclick = function () { player.toggle(); };
35    } else if (p.loading) np.textContent = "Threading the tape...";
36    else if (p.error) np.innerHTML = esc(p.error) + (p.show ? ' <a href="' + esc(p.show.details) + '" target="_blank" rel="noopener">Open it on the archive &#8599;</a>' : "");
37    else if (p.track) np.innerHTML = "&#9835; " + esc(p.track.title) + " &middot; " + esc(p.show.date) + " " + esc(p.show.venue || "") +
Fetch the night's songs from the server and play the first; when a song ends, the next one starts.

One game, inside the browser page

On a computer, the game opens in the middle of the browser page, framed like a window inside the page. On a phone, it opens on its own page, since a phone screen is too small for both. The color you pick in the browser is saved in the same place the game keeps its own color setting, so both always match.

The ideas to keep
  • The server prepares the tour, rules and ticket before the game starts.
  • The game announces events; the page listens and handles playlists and leaderboards.
  • On desktop the game sits inside the browser page; on phones it gets the whole screen.