Making a Speedrun Timer
Create a speedrun timer UI that starts at the first checkpoint and stops at the finish. Build UI with J.uiElement, expose init/start/stop helpers, update every tick using world time, and integrate via checkpoint collisions.
Script
Hey everyone, and welcome to Episode 8 of Jamango Academy! Time is flying, so let’s build a speedrun timer for our obby. We’ll create UI text, start it at the first checkpoint, and stop it at the finish.
Initialize timer UI (timer.ts)#
Create a function to set up the UI, placing a text element we can update.
tsimport * as J from "jamango"; export let timerText: HTMLElement; let startTime = 0; let timerRunning = false; export function initUI() { J.uiElement.innerHTML = ` <div class="absolute size-full p-4 text-white"> <p class="timer absolute left-12 text-5xl font-medium" style="top:120px;font-family:'Luckiest Guy'">⏲ 0:00</p>
Start and stop helpers#
Expose functions other scripts can call to control the timer.
tsexport function startTimer() { startTime = J.getWorldTime(); timerRunning = true; } export function stopTimer() { timerRunning = false; }
Update the display each tick#
Only update when the timer is running. Format as mm:ss with a leading zero for seconds.
tsJ.onGameTick((_, currentTime) => { if (timerRunning == false) return; const elapsedTime = currentTime - startTime; const minutes = Math.floor(elapsedTime / 60); const seconds = Math.floor(elapsedTime % 60); const formattedTime = `${minutes}:${seconds.toString().padStart(2, "0")}`; timerText.innerText = `⏰ ${formattedTime}`; });
Hook into checkpoints#
Start at checkpoint 0 (the first jump), stop at the final checkpoint (e.g. 4).
ts// respawn.ts import * as TimerUI from "./timer"; J.onEntityCollisionStart( { source: [PlayerCheckpointTrait], target: [CheckpointTrait], }, (sourceId, targetId) => { const checkpointTrait = J.getTrait(targetId, CheckpointTrait);
That’s it! You’ve got a clean, readable speedrun timer that updates every frame and integrates with your existing checkpoint system. Compete with friends and share your best times!