AstroBlast is a single-page vector shooter in the Asteroids tradition: rotate, thrust, fire, and break rocks into smaller rocks until one of them ends you. It is delivered as one static HTML document with no build step, no bundler, and no runtime network calls.
None. Deliberately. The entire game - markup, styling, simulation, rendering, and audio -
is contained in index.html. There are no JavaScript frameworks, no CSS
frameworks, no web fonts, no analytics, and no CDN references.
| Capability | Provided by | Fallback |
|---|---|---|
| Rendering | Canvas 2D (CanvasRenderingContext2D) | None required - universally supported |
| Frame pacing | requestAnimationFrame | None required |
| Sound | Web Audio API (procedural, no audio files) | Game runs silently if unavailable |
| High score | localStorage | Score is session-only if blocked |
| Touch controls | Pointer Events + matchMedia('(pointer:coarse)') | Keyboard only |
| Icon | Inline SVG data URI | Browser default icon |
The physics runs on a fixed 120 Hz timestep accumulated against the
requestAnimationFrame delta, so behaviour is identical on 60 Hz, 120 Hz, and
144 Hz displays. The rAF delta is clamped to 250 ms, which prevents a backgrounded tab
from replaying several seconds of simulation in one frame on return.
Motion is Newtonian with exponential velocity decay (v *= exp(-drag*dt))
rather than per-frame multiplication, again so damping is step-size independent. All
entities wrap toroidally on the viewport bounds. Collision detection is circle-circle
against each entity's bounding radius; asteroid polygons are cosmetic.
| Target | Points |
|---|---|
| Large asteroid | 20 |
| Medium asteroid | 50 |
| Small asteroid | 100 |
| Large saucer | 200 |
| Small saucer | 1,000 |
| Extra life | every 10,000 points |
Asteroids destroyed by a saucer's shots award nothing - only your own shots score. Small saucers fire with tight accuracy and become more common as your score climbs.
AstroBlast collects nothing. There is no account system, no telemetry, no cookies, and
no third-party script. Your high score is a single integer in localStorage
under the key astroblast.highscore, stored in your browser and never
transmitted. Clearing site data removes it permanently.
The site is served from a private Amazon S3 bucket through an Amazon CloudFront
distribution using Origin Access Control. The bucket has all public access blocked and
its policy admits only the CloudFront distribution's service principal, scoped by
AWS:SourceArn. All traffic is redirected to HTTPS, and a response headers
policy applies HSTS, a restrictive Content-Security-Policy, X-Content-Type-Options:
nosniff, frame-ancestors 'none', and a strict referrer policy.
Infrastructure is defined in CloudFormation; nothing was created by hand.
The canonical hostname is astroblast.andrewnorman.org, served under a
DNS-validated ACM certificate with SNI and a minimum protocol version of TLS 1.2
(CloudFront's TLSv1.2_2021 policy, which drops every SHA-1 and CBC cipher
suite). Route 53 A and AAAA alias records point the name at the distribution, so it
resolves over both IPv4 and IPv6. The generated *.cloudfront.net hostname
remains reachable as well.
connect-src 'none'. If the game ever tried to
phone home, the browser would block it.