I built an arcade game with real orbital mechanics, zero dependencies and no build step
I built an arcade game with real orbital mechanics, zero dependencies and no build step SKYHOOK is a one-touch climbing game. Your rocket is tethered to a planet or a star. You tap once; the thruster fires, the tether
I built an arcade game with real orbital mechanics, zero dependencies and no build step
SKYHOOK is a one-touch climbing game. Your rocket is tethered
to a planet or a star. You tap once; the thruster fires, the tether releases, you fly in
a straight line, and you auto-latch onto the next body you pass near. Miss everything and
you drift off the screen. That is the only way a run ends.
One input. No install. It also runs from file://.
Source: https://github.com/leopechnicki/skyhook
The decision that made the game
The obvious way to build this is a single global orbit speed and a single swing radius.
It works and it is boring, because every body in the sky plays identically and the
player has nothing to read.
So the orbit is derived from the body instead. Every body gets a mass and a
radius, and the rest falls out of Kepler:
const mu = G * body.mass // standard gravitational parameter
const omega = Math.sqrt(mu / (r**3)) // orbital angular rate
const v = Math.sqrt(mu / r) // = omega * r, the speed you leave with
The relations are the real ones. Only the constants are tuned.
Three things came free the moment I did that:
1. The sky became self-documenting. A body's circle, its latch ring and its orbit
width all scale with its radius, and radius correlates with mass. So a star looks
heavy, and it is heavy: it spins you faster and slings you roughly 1.6x further than a
planet. I never had to write a tutorial line explaining the difference, or colour-code a
danger level. The physics and the art are the same signal.
2. Difficulty got a free axis. To make the game harder with altitude I did not touch
timing, speed, or input windows. I changed the population of the sky - more stars, more
mass. The release window tightens on its own, because a heavier body spins you faster.
Emergent difficulty is much cheaper to tune than hand-authored difficulty.
3. Tight hooks became meaningful. v = sqrt(mu/r) means catching a body closer in
gives you more speed. The scoring rewards a tight latch (15 pts vs 8, and it builds a
combo up to x9), so the mechanically greedy play and the physically interesting play are
the same play. That alignment is most of what makes a score-chaser feel good.
The constraint: zero dependencies, zero build step
No npm install, no bundler, no framework. index.html, a handful of ES modules, a canvas.
This is not purity for its own sake. It buys concrete things:
- The game loads in one round trip and runs on a cheap phone.
- It works offline, from a downloaded folder, over
file://. - There is no supply chain. A game page executing third-party CDN code is a bad trade.
- In five years it will still run, because there is no toolchain to rot.
The constraint got properly tested when I added an online leaderboard with Supabase.
The documented path is npm install @supabase/supabase-js, or an ESM import from a CDN.
Either one breaks three of the four properties above.
So the game talks to the GoTrue HTTP API directly:
// sign-up, sign-in, password recovery - all plain fetch
await fetch(`${SUPABASE_URL}/auth/v1/signup`, {
method: 'POST',
headers: { apikey: ANON_KEY, 'Content-Type': 'application/json' },
body: JSON.stringify({ email, password }),
})
Password recovery is POST /auth/v1/recover, then PUT /auth/v1/user carrying the
recovery bearer token. That is the whole auth layer. No SDK, no build step, and the same
file still opens from disk.
The lesson generalises: an SDK is a convenience wrapper over an HTTP API, and when the
wrapper costs you a property you actually care about, read the API docs and write the
twelve lines yourself.
Things I got wrong
- The first death. Early testers did not know the whole screen was the button. The title screen now says it in one line, which was worth more than any tutorial.
- A dead-end sign-in form. The first leaderboard build had exactly two error messages and no password reset. A player locked herself out and made a second account instead - which is the clearest possible bug report. Added recovery.
- Meteoroids in the flight path. The first pass spawned them anywhere, which punished correct play. They now sit off the direct line between two bodies, so a clean release is always safe and only a greedy detour clips one.
Play it
https://skyhookplay.com - desktop or mobile, one tap, no install.
Add ?seed=123 to replay a deterministic layout.
Code: https://github.com/leopechnicki/skyhook
Originally published by Dev.to WebDev. Aggregated on AIWithGhost for educational purposes β full credit and traffic to the original publisher.