Skip to content

Latest commit

 

History

1 Commit

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 

Repository files navigation

Switchback

One tap. Two rings. Don't clip a bar.

A minimal arcade game in a single HTML file. No build step, no dependencies, no framework — open it in a browser and it runs.

Built by AnbuTech.


The game

You're pinned to the top of the screen while the orbit spins past you. Tap to jump between the inner and outer ring. Dark bars block one ring at a time — switch before you reach them. Gold pips are worth more than survival.

Action Reward
Pass a bar +1
Collect a pip +3
Clip a bar Run over

Speed scales with your score, capped so it stays playable rather than becoming a coin flip.

Controls

Input Effect
Tap / click anywhere Switch ring (or start / restart)
Space, , Enter Same

One input, one meaning, in every game state. There is nothing else to learn.

Running it

git clone https://github.com/<your-org>/switchback.git
cd switchback
open switchback.html      # or just drag it into a browser

For local testing on a phone, serve it over your network:

python3 -m http.server 8000
# then visit http://<your-lan-ip>:8000/switchback.html

How it works

The core trick is that the player never moves angularly. Instead of orbiting the player around the circle, the player is fixed at the top (TOP = -π/2) and the entire world rotates beneath. Every obstacle stores a w value — its position along an ever-increasing world angle — and renders at TOP + (w - dist), where dist is how far the world has spun.

This makes collision detection trivial and stable. There is no wrapping, no modular arithmetic, no drift. A bar is dangerous when |w - dist| is small and the player's current radius is close to that bar's ring radius:

const near = Math.abs(b.w - G.dist) < b.hw + pAng;
if (near && Math.abs(G.r - ringR[b.ring]) < BAR_W / 2 + PLAYER_R + 2) die();

Because the radius is a live tweened value rather than a discrete ring index, a switch made too late can clip either ring. That is intentional — committing early is the skill the game is actually testing.

Obstacles are generated lazily ahead of the player and culled behind, so memory stays flat no matter how long the run lasts.

Rendering

Single <canvas>, scaled for device pixel ratio (capped at 2x to spare low-end GPUs). Everything is drawn per frame with requestAnimationFrame and a delta clamped to 33 ms, so a background tab or a stutter can't teleport the player through a bar.

Best-score persistence

loadBest() / saveBest() currently call window.storage, an async key–value API that exists in the environment this prototype was authored in. Both calls are wrapped in try/catch and fail silently, so the game runs fine without it — the score just won't survive a reload.

Before shipping, swap in localStorage:

function loadBest(){
  G.best = parseInt(localStorage.getItem('switchback:best') || '0', 10);
  elBest.textContent = G.best;
}
function saveBest(){
  localStorage.setItem('switchback:best', String(G.best));
}

Tuning

Everything that affects feel lives at the top of the script or in update():

Constant Default Effect
PLAYER_R 9 Player size, and therefore hitbox generosity
BAR_W 14 Bar thickness — raises collision tolerance too
ringR R*0.50, R*0.74 Ring separation; wider means longer, riskier switches
G.tween + dt * 8 8 Switch speed. Lower is floatier, higher is snappier
1.5 + min(1.75, score*0.012) Difficulty ramp: base speed, ceiling, and rate
gapFor() 1.45 → 0.82 Angular spacing between bars as score climbs

If the game feels unfair, raise the ramp ceiling last. Nine times out of ten the problem is spacing, not speed.

Design

Token Value Use
Field #1F3AC4 Background
Deep #152C96 Ring shadow
Ink #0B1440 Bars
Chalk #F2EFE4 Rings, type
Signal #FF4D8D Player, trail
Solar #FFC93C Pips, accents

Type is Bricolage Grotesque for display and IBM Plex Mono for telemetry. prefers-reduced-motion disables screen shake and the pulsing prompt.

Roadmap

  • Replace window.storage with localStorage
  • Wrap with Capacitor for an Android build
  • AdMob: interstitial on every third game over, rewarded video for a one-time continue
  • Audio: a short tick on switch, a thud on death
  • Daily seed mode for comparable scores

Contact

AnbuTech — an elite engineering unit that ships. anbu-tech.com · LinkedIn


Copyright

Copyright © 2026 AnbuTech. All rights reserved.

This software and its source code are the proprietary property of AnbuTech. No part of this repository may be reproduced, distributed, modified, or used to create derivative works without prior written permission from AnbuTech.

About

A minimal orbit-dodging arcade game built as one self-contained HTML file — no framework, no build pipeline, no assets to load. Canvas rendering, delta-clamped game loop, and a rotating-world trick that makes collision detection stable at any speed.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages