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:
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)
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:
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 });
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:
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">▶ 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 ↗</a>' : ""); 37 else if (p.track) np.innerHTML = "♫ " + esc(p.track.title) + " · " + esc(p.show.date) + " " + esc(p.show.venue || "") +
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 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.