{"id":51011921,"url":"https://github.com/vdirienzo/neon-serpent","last_synced_at":"2026-06-21T04:01:19.171Z","repository":{"id":364483837,"uuid":"1268074390","full_name":"vdirienzo/neon-serpent","owner":"vdirienzo","description":"NEØN SERPENT — 3D snake game with synthwave aesthetics. Three.js, WebGL2, TypeScript, Vite.","archived":false,"fork":false,"pushed_at":"2026-06-20T08:33:09.000Z","size":816,"stargazers_count":0,"open_issues_count":4,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-20T10:14:56.541Z","etag":null,"topics":["3d","es-modules","game","no-build-step","pwa","snake","synthwave","threejs","typescript","vite","webgl2"],"latest_commit_sha":null,"homepage":"https://vdirienzo.github.io/neon-serpent/","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/vdirienzo.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":".github/CONTRIBUTING.md","funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":"audit/findings/findings.sarif.json","citation":null,"codeowners":".github/CODEOWNERS","security":"SECURITY.md","support":"SUPPORT.md","governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null},"funding":{"github":"vdirienzo"}},"created_at":"2026-06-13T05:43:16.000Z","updated_at":"2026-06-13T14:10:53.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/vdirienzo/neon-serpent","commit_stats":null,"previous_names":["vdirienzo/neon-serpent"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/vdirienzo/neon-serpent","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vdirienzo%2Fneon-serpent","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vdirienzo%2Fneon-serpent/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vdirienzo%2Fneon-serpent/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vdirienzo%2Fneon-serpent/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/vdirienzo","download_url":"https://codeload.github.com/vdirienzo/neon-serpent/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/vdirienzo%2Fneon-serpent/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":34593129,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-21T02:00:05.568Z","response_time":54,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["3d","es-modules","game","no-build-step","pwa","snake","synthwave","threejs","typescript","vite","webgl2"],"created_at":"2026-06-21T04:01:18.136Z","updated_at":"2026-06-21T04:01:19.158Z","avatar_url":"https://github.com/vdirienzo.png","language":"JavaScript","funding_links":["https://github.com/sponsors/vdirienzo"],"categories":[],"sub_categories":[],"readme":"# NEØN SERPENT\n\n[![CI](https://github.com/vdirienzo/neon-serpent/actions/workflows/ci.yml/badge.svg)](https://github.com/vdirienzo/neon-serpent/actions/workflows/ci.yml)\n[![CodeQL](https://github.com/vdirienzo/neon-serpent/actions/workflows/codeql.yml/badge.svg)](https://github.com/vdirienzo/neon-serpent/actions/workflows/codeql.yml)\n[![Deploy](https://github.com/vdirienzo/neon-serpent/actions/workflows/deploy.yml/badge.svg)](https://github.com/vdirienzo/neon-serpent/actions/workflows/deploy.yml)\n[![Vercel](https://img.shields.io/badge/Deploy-Vercel-000000.svg)](https://vercel.com/new/clone?repository-url=https%3A%2F%2Fgithub.com%2Fvdirienzo%2Fneon-serpent)\n[![License: MIT](https://img.shields.io/badge/License-MIT-00f6ff.svg)](./LICENSE)\n[![Node ≥ 18](https://img.shields.io/badge/node-%E2%89%A518-39ff14.svg)](https://nodejs.org)\n[![TypeScript](https://img.shields.io/badge/TypeScript-5.6-3178c6.svg)](https://www.typescriptlang.org)\n[![Vite](https://img.shields.io/badge/Vite-5.4-646cff.svg)](https://vitejs.dev)\n[![Three.js](https://img.shields.io/badge/Three.js-r149-black.svg)](https://threejs.org)\n[![Tests](https://img.shields.io/badge/tests-438%20passing-brightgreen.svg)](./tests)\n[![Coverage](https://img.shields.io/badge/coverage-target%2070%25-yellow.svg)](./vitest.config.ts)\n[![Code style: Prettier](https://img.shields.io/badge/code_style-prettier-ff69b4.svg)](https://prettier.io)\n[![Linter: ESLint](https://img.shields.io/badge/linter-eslint-4b32c3.svg)](https://eslint.org)\n[![Security: CodeQL](https://img.shields.io/badge/security-CodeQL-blueviolet.svg)](./.github/workflows/codeql.yml)\n[![Maintenance](https://img.shields.io/badge/maintenance-active-brightgreen.svg)](./.github/CODEOWNERS)\n[![PRs Welcome](https://img.shields.io/badge/PRs-welcome-ff2bd6.svg)](./.github/CONTRIBUTING.md)\n\n\u003e A 3D snake game with synthwave/neon aesthetics, WebGL2 rendering,\n\u003e adaptive audio, three game modes, and full keyboard / touch /\n\u003e accessibility support.\n\nNEØN SERPENT is a single-page browser game. It is **pure ES modules\nwith no build step**: open `index.html` over HTTP and play. There\nis no `npm install`, no bundler, no transpiler.\n\nThe codebase is a 2026 SOTA refactor of an original 3 700-line\nmonolithic HTML file into **75+ focused modules**, a modular CSS\narchitecture, a small set of Web Components, and a 376-test suite\nwith ≈ 92 % coverage of `src/`.\n\n---\n\n## Quick start (30 seconds)\n\n```bash\n# 1. Clone (or download) the project\ncd opensake\n\n# 2. Start the dev server (Python's built-in http.server is enough)\n./serve.sh\n# → http://localhost:8765/\n\n# 3. Play. That's it.\n```\n\n\u003e The game **must** be served over HTTP. `file://` URLs do not work\n\u003e for ES module imports. Any static server works (`python3 -m\n\u003e http.server`, `npx serve`, `caddy file-server`, …).\n\n### Run the tests\n\n```bash\nnode --test tests/\n```\n\nThat's the whole test command. The runner is built into Node 18+.\n\n---\n\n## Controls\n\n### Movement (camera-relative)\n\n| Key               | Action         |\n| ----------------- | -------------- |\n| `W` / `ArrowUp`   | Forward        |\n| `S` / `ArrowDown` | Backward       |\n| `A` / `ArrowLeft` | Strafe left    |\n| `D` / `ArrowRight`| Strafe right   |\n\n\u003e Directions are remapped to the **current camera basis**, so\n\u003e \"forward\" always means \"into the screen\" regardless of camera\n\u003e mode.\n\n### Game flow\n\n| Key                          | Action                                       |\n| ---------------------------- | -------------------------------------------- |\n| `P` / `Enter` / `Space`      | Pause / Start / Restart (state-dependent)    |\n| `Esc`                        | Back to title (or close any open modal)      |\n\n### Camera, audio, calibration\n\n| Key  | Action                                            |\n| ---- | ------------------------------------------------- |\n| `C`  | Cycle camera (CINEMÁTICA → CENITAL → PERSECUCIÓN) |\n| `M`  | Toggle audio on / off                            |\n| `L`  | Open / close the **Calibration panel** (3D + CSS) |\n| `O`  | Open / close the **Settings modal**              |\n\n### Level select\n\n| Key      | Action                                                       |\n| -------- | ------------------------------------------------------------ |\n| `J`      | Open the **Level Select** modal (5×2 grid)                   |\n| `1` – `9` | Jump directly to level N                                     |\n| `0`      | Jump directly to level 10                                    |\n\n### Touch\n\nSwipe in any direction (≥ 26 px) to steer. The first time you load\nthe game on a touch device, a hint is shown for ~3.5 s.\n\nSee [docs/ACCESSIBILITY.md](./docs/ACCESSIBILITY.md) for the full\na11y contract: focus management, reduced motion, color-blind\npalettes, and screen-reader announcements.\n\n---\n\n## Features\n\n- **10 hand-crafted levels** with three difficulty phases\n  (EASY → MEDIUM → HARD → 3D). See\n  [docs/LEVELS.md](./docs/LEVELS.md).\n- **Procedural terrain** with FBM noise — islands, bridges,\n  spiral staircases, and stacked 3D platforms.\n- **3 camera modes**: cinematic (orbiting), top-down, and chase\n  (shoulder cam). Damped blending between modes, with screen\n  shake on death.\n- **Adaptive music** — sawtooth bass + pad + arp loop, with\n  hi-hat and sub-bass layers that fade in as the snake grows.\n- **3 game modes**:\n  - **HISTORIA** — play through all 10 sectors with infinite\n    continuation after the 10th.\n  - **TIME ATTACK** — score as much as you can in 60 s.\n  - **DAILY SEED** — one procedurally generated level per day\n    (`YYYYMMDD` seed), the same for every player worldwide.\n- **3D platforms** with multi-floor climbs (levels 8–10).\n- **Pickups**: `orb` (10), `gem` (50), `crystal` (speed 6 s, 25),\n  `slow` (slow-mo 4 s, 20), `dice` (200 + skip to next level).\n- **Bonus nucleus** — gold multi-collect orb that spawns every 4\n  food pickups.\n- **Calibration panel** — runtime-tunable lighting, tone mapping,\n  fog, CRT overlay, vignette, pixel ratio, snake emissive, water\n  opacity. Persisted in `localStorage`.\n- **Color-blind modes** (deuteranopia, protanopia, tritanopia)\n  via CSS custom-property swap.\n- **Reduced motion** — globally disables glitch / pop / shake\n  animations; gated from both CSS (`@media`) and JS\n  (`Haptics.setReducedMotion`).\n- **Slow mode (0.7×)** — global difficulty assist.\n- **Haptics** — 10 named vibration patterns on touch devices.\n- **Leaderboard** — top 10 scores + per-sector bests.\n- **Share** — clipboard API with `document.execCommand('copy')`\n  fallback.\n- **Web Components** — Shadow-DOM `\u003cns-button\u003e`, `\u003cns-modal\u003e`,\n  `\u003cns-chip\u003e`, `\u003cns-panel\u003e`, `\u003cns-toast\u003e`, `\u003cns-progress-bar\u003e`.\n- **CSS architecture** — tokens, `@layer`, `color-mix()`,\n  container queries, BEM-light.\n- **PWA** — `manifest.webmanifest` + `sw.js` service worker for\n  offline play.\n- **i18n-ready** — Spanish UI today, English locale scaffolded for\n  the next drop.\n\n---\n\n## Tech stack\n\n| Concern        | Choice                                                  |\n| -------------- | ------------------------------------------------------- |\n| Language       | JavaScript (ES2020 modules, no transpiler)              |\n| 3D             | Three.js `r149` via importmap (unpkg)                   |\n| Post-fx        | `EffectComposer` + `UnrealBloomPass` + `GammaCorrectionShader` |\n| Audio          | Native Web Audio (synth + busses)                       |\n| Haptics        | `navigator.vibrate`                                     |\n| Persistence    | `localStorage` (with in-memory fallback)                |\n| CSS            | Native, no preprocessor — `@layer`, custom properties, `color-mix()` |\n| Tests          | `node:test` (built into Node 18+) + a tiny in-house DOM mock + a Three.js stub |\n| Coverage       | `c8` (V8 inspector, optional install)                  |\n| Dev server     | `python3 -m http.server` (the bundled `serve.sh`)       |\n| PWA            | `manifest.webmanifest` + `sw.js`                        |\n\n### Why no bundler?\n\nThe 2026 refactor **deliberately** dropped the toolchain. ES\nmodules + importmap cover all the cases that previously required\nWebpack / Vite / esbuild, and the codebase is small enough that\nthe per-module HTTP request is fine. The trade-off is no TS and\nno JSX; the win is \"open in 5 seconds, no `node_modules`\".\n\n---\n\n## Project layout\n\n```\nopensake/\n├── index.html              # Entry: importmap + module script\n├── manifest.webmanifest    # PWA manifest\n├── sw.js                   # Service worker (cache-first)\n├── serve.sh                # python3 -m http.server wrapper\n├── package.json            # Scripts + metadata (no deps)\n├── src/\n│   ├── main.js             # Bootstrap\n│   ├── config.js           # Frozen constants (GRID, palette, EVT, …)\n│   ├── core/               # Foundation (math, store, event-bus, …)\n│   ├── render/             # Three.js setup\n│   ├── world/              # Heightmap, noise, level builders\n│   ├── entities/           # Snake, food, bonus, pickups, …\n│   ├── game/               # State machine, step logic, scoring\n│   ├── camera/             # 3 camera modes + controller\n│   ├── input/              # Keyboard, touch, camera-relative mapping\n│   ├── audio/              # Web Audio synthwave + SFX + haptics\n│   ├── components/         # Shadow-DOM Web Components (ns-button, …)\n│   ├── ui/                 # HUD, modals, calibration, level select\n│   └── styles/             # Modular CSS (tokens, base, layout, fx, …)\n├── tests/                  # node:test suite\n│   ├── unit/  unit/extra/  # Pure logic\n│   ├── dom/                # UI logic with the DOM mock\n│   ├── three/              # Three.js with the WebGL stub\n│   ├── integration/        # Cross-module flows\n│   └── smoke/              # File existence + import resolution\n└── docs/\n    ├── CONTRIBUTING.md     # Dev setup, code style, PR process\n    ├── MODULES.md          # One-paragraph per directory + exports\n    ├── LEVELS.md           # 10 levels, palettes, how to add one\n    ├── TESTING.md          # Test pyramid, mock patterns, coverage\n    └── ACCESSIBILITY.md    # Keyboard map, ARIA, screen reader\n```\n\n---\n\n## Testing\n\n```bash\n# Whole suite (CI)\nnode --test tests/\n\n# Explicit grouping (faster feedback in dev)\nnode --test \\\n  tests/unit/*.test.js \\\n  tests/unit/extra/*.test.js \\\n  tests/integration/*.test.js \\\n  tests/dom/*.test.js \\\n  tests/three/*.test.js \\\n  tests/smoke/*.test.js\n\n# One file\nnode --test tests/unit/terrain.test.js\n\n# One test by name\nnode --test --test-name-pattern=\"buildLevel1\" tests/unit/terrain.test.js\n```\n\nCoverage (optional):\n\n```bash\nnpm i -g c8\nc8 --reporter=text --reporter=html node --test tests/\nopen coverage/index.html\n```\n\nCurrent state: **376 tests passing**, **≈ 92 %** coverage of\n`src/`. See [docs/TESTING.md](./docs/TESTING.md) for the full\nstrategy, mock patterns, and per-directory targets.\n\n---\n\n## Documentation\n\n| File                                       | Topic                                  |\n| ------------------------------------------ | -------------------------------------- |\n| [README.md](./README.md)                   | This file — quick start + features     |\n| [ARCHITECTURE.md](./ARCHITECTURE.md)       | Module map, state machine, event bus   |\n| [docs/CONTRIBUTING.md](./docs/CONTRIBUTING.md) | Dev setup, code style, PR process  |\n| [docs/MODULES.md](./docs/MODULES.md)       | Per-directory deep dive + exports      |\n| [docs/LEVELS.md](./docs/LEVELS.md)         | 10 levels, palettes, how to add one    |\n| [docs/TESTING.md](./docs/TESTING.md)       | Test pyramid, mock patterns, coverage  |\n| [docs/ACCESSIBILITY.md](./docs/ACCESSIBILITY.md) | Keyboard, ARIA, a11y contract  |\n| [CHANGELOG.md](./CHANGELOG.md)             | Per-version history (2.0.0, 3.0.0)     |\n\n---\n\n## Contributing\n\nWe welcome PRs. The non-negotiables:\n\n1. **Run `node --test tests/`** before pushing. The suite must\n   pass.\n2. **Add tests for new logic.** See\n   [docs/TESTING.md](./docs/TESTING.md) for the pattern.\n3. **JSDoc every new public symbol** — `@param`, `@returns`, and\n   an `@example` when the call site isn't obvious.\n4. **Update the relevant `docs/` file** when you add a module, a\n   level, a control, or an accessibility-relevant change.\n5. **No `console.log`** in production code. Use `log` from\n   `core/Log.js` (level-gated) or remove the line.\n\nRead [docs/CONTRIBUTING.md](./docs/CONTRIBUTING.md) for the\ncommit-message format, the module-ownership map, and the PR\nchecklist.\n\n---\n\n## License\n\nMIT. See `LICENSE` (when present) or rely on the standard MIT\nterms. The original monolith (`neon-serpent.html.bak`) is kept for\nhistorical comparison and is not part of the active codebase.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvdirienzo%2Fneon-serpent","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fvdirienzo%2Fneon-serpent","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fvdirienzo%2Fneon-serpent/lists"}