- JavaScript 68.2%
- HTML 16.2%
- PowerShell 15.6%
| .claude | ||
| assets | ||
| web | ||
| .gitattributes | ||
| .gitignore | ||
| API.md | ||
| build.ps1 | ||
| CHANGELOG.md | ||
| CLAUDE.md | ||
| README.md | ||
Nyancat Lost In Space - Redux
A rework of the 2011 Flash game, playable in any web browser without Flash (via Ruffle) and with a new highscore system backed by GlobalRanks.
The original SWF was exported with JPEXS FFDec into assets/. The menus were
stripped of obsolete links/banners and the legacy MyLostGames highscore calls
were replaced with a GlobalRanks integration driven by a small JavaScript
bridge. build.ps1 recompiles the edited ActionScript back into a SWF that is
served by Ruffle.
Repository layout
| Path | Purpose |
|---|---|
assets/NyanCat.swf |
Original, unmodified game (build input — do not edit) |
assets/scripts/ |
FFDec-exported ActionScript; the edited sources |
build.ps1 |
Recompiles assets/scripts into web/NyanCat.swf |
web/index.html |
Ruffle host page |
web/nyan-ranks.js |
JS bridge: GlobalRanks REST ↔ SWF (ExternalInterface) |
web/NyanCat.swf |
Build output (generated; serve this) |
API.md |
GlobalRanks REST API reference |
Prerequisites
| Tool | Version | Notes |
|---|---|---|
| JPEXS FFDec | recent | Provides ffdec-cli.exe; recompiles the AS3 |
| Java (JRE/JDK) | 11+ | Required by FFDec; verified here with Java 21 |
| PowerShell | 7+ | build.ps1 declares #Requires -Version 7 |
| A static web server | any | Must serve web/ over HTTPS (see Hosting) |
| A reachable GlobalRanks instance | — | Default: https://ranks.flappybird2026.com/ |
FFDec is auto-located from PATH or the default install dirs; otherwise pass
-FFDec "C:\path\to\ffdec-cli.exe" to build.ps1.
Build
pwsh ./build.ps1
# or, with an explicit FFDec path:
pwsh ./build.ps1 -FFDec "C:\Program Files (x86)\FFDec\ffdec-cli.exe"
This re-imports assets/scripts into a copy of assets/NyanCat.swf and writes
web/NyanCat.swf. FFDec recompiles the ActionScript, so the build also acts as
the compile check — a non-zero exit means an AS3 error (see the output).
Configuration
Edit web/nyan-ranks.js if the backend differs:
API_BASE— GlobalRanks API root (defaulthttps://ranks.flappybird2026.com/api/v1).GAME_SLUG— leaderboard key (defaultnyan-cat-lost-in-space; lowercase[a-z0-9_-]).
The bridge stores the per-user UUID and secret token in localStorage and
sends them as X-User-UUID / X-Auth-Token. It refuses to use the auth
token on a non-HTTPS origin (localhost excepted); leaderboard reads still
work, but score submission and renames require HTTPS.
GlobalRanks username behaviour
Two server-side traits of GlobalRanks affect the in-game "Change Name" panel (the client surfaces both with specific messages — it cannot work around them):
- Usernames are unique across the whole GlobalRanks instance, not per
game. A name already taken by any user of any game registered against
that deployment returns
409→ the panel shows "NAME ALREADY TAKEN". Auto-assignedPlayer_xxxxxxnames never collide; pick a distinctive custom name. - A user profile only exists after the first score is submitted. Until
then
GET /users/{uuid}is404: the panel shows "NAME SET AFTER 1ST SCORE", and a rename attempt shows "PLAY A ROUND FIRST". Play one round, then rename.
Hosting
- Run the build so
web/NyanCat.swfexists. - Serve the
web/directory with any static web server, over HTTPS (browsers and the bridge both require a secure context). - The GlobalRanks server must send CORS headers permitting your page's
origin (
Access-Control-Allow-Origin, and theX-User-UUID/X-Auth-Tokenrequest headers). That is configured on the GlobalRanks deployment, not here.
Ruffle (CDN vs. self-hosted)
web/index.html loads Ruffle from the CDN pinned to an exact version
(@ruffle-rs/ruffle@0.2.0). For production, self-hosting is recommended
— it removes the third-party dependency entirely:
- Download the
*-selfhosted.zipfor a release from https://github.com/ruffle-rs/ruffle/releases. - Extract it into
web/ruffle/. - In
web/index.html, change the Ruffle<script src>to./ruffle/ruffle.js(dropcrossorigin).
If you keep the CDN, add a Subresource Integrity hash to the <script>:
integrity="sha384-…" computed from that exact pinned artifact. Re-pin and
recompute the hash when bumping Ruffle.
Security response headers (required for production)
Set these at the web server. A Content-Security-Policy meaningfully limits the
blast radius of any XSS, but Ruffle's CSP needs care and must be validated
in a browser — Ruffle compiles WebAssembly and spawns a blob worker, and
index.html currently contains an inline <script>/<style>.
Working starting point (self-hosted Ruffle; everything same-origin):
Content-Security-Policy:
default-src 'none';
script-src 'self' 'wasm-unsafe-eval' 'unsafe-inline';
style-src 'self' 'unsafe-inline';
connect-src 'self' https://ranks.flappybird2026.com;
img-src 'self' data:;
worker-src blob:;
object-src 'none';
base-uri 'none';
frame-ancestors 'none'
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
Referrer-Policy: no-referrer
Strict-Transport-Security: max-age=63072000; includeSubDomains
Notes:
- If you keep the CDN instead of self-hosting, add
https://unpkg.comto bothscript-srcandconnect-src(Ruffle fetches its.wasmfrom there). 'unsafe-inline'is required only becauseindex.htmlhas an inline bootstrap script and stylesheet. To drop it, move that JS/CSS into separate files underweb/(or apply CSP hashes/nonces) and tightenscript-src/style-srcto'self'.Strict-Transport-Securityonly once the site is reliably HTTPS.
Example — Caddy:
ranks-game.example.com {
root * /srv/nyancat/web
file_server
header {
Content-Security-Policy "default-src 'none'; script-src 'self' 'wasm-unsafe-eval' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; connect-src 'self' https://ranks.flappybird2026.com; img-src 'self' data:; worker-src blob:; object-src 'none'; base-uri 'none'; frame-ancestors 'none'"
X-Content-Type-Options "nosniff"
X-Frame-Options "DENY"
Referrer-Policy "no-referrer"
Strict-Transport-Security "max-age=63072000; includeSubDomains"
}
}
Example — nginx:
location / {
root /srv/nyancat/web;
add_header Content-Security-Policy "default-src 'none'; script-src 'self' 'wasm-unsafe-eval' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; connect-src 'self' https://ranks.flappybird2026.com; img-src 'self' data:; worker-src blob:; object-src 'none'; base-uri 'none'; frame-ancestors 'none'" always;
add_header X-Content-Type-Options "nosniff" always;
add_header X-Frame-Options "DENY" always;
add_header Referrer-Policy "no-referrer" always;
add_header Strict-Transport-Security "max-age=63072000; includeSubDomains" always;
}
Development notes
- Only
assets/scripts/**is hand-edited;assets/NyanCat.swfis the pristine build input. Re-runbuild.ps1after any script change. - Pre-commit gate: run
/reviewand/security-auditand resolve findings before committing (seeCLAUDE.md). Record changes inCHANGELOG.md. - See
CHANGELOG.md"Security" for the tracked, deferred hardening items (SRI, CSP/header rollout) referenced above.