⛧ NEON JUMPA neon-cyberpunk vertical jumper that lives inside your editor. Bounce up an endless synthwave skyline, drawn frame-by-frame by a hand-written WebGL2 pipeline — SDF neon platforms, HDR bloom, a perspective grid, chromatic aberration, CRT curvature, scanlines and grain. No game engine. Zero runtime dependencies.
WhyIt's the classic "jump forever, don't fall" arcade loop — but every pixel is rendered by a custom WebGL2 renderer running inside a VS Code / Cursor webview. It's a love letter to synthwave and a compact showcase of real-time graphics techniques in a strictly-typed TypeScript codebase bundled with zero runtime dependencies. Features
Install & run (from source)
Open the folder in VS Code or Cursor and press F5 to launch an Extension Development Host. In that new window, open the Command Palette (Cmd/Ctrl+Shift+P) and run:
Package as a
|
| Key | Action |
|---|---|
| Space | Start / restart |
| ← / → | Move left / right |
| A / D | Move left / right |
You wrap around the screen edges. Touching the bottom edge ends the run.
How to play
Jump as high as you can — the score is your altitude. Platforms spawn on a fixed vertical grid sized to your jump height (clearing one empty row costs about 0.9 of a full jump).
| Platform | Colour | Behaviour |
|---|---|---|
| Stable | cyan | Never breaks. (~50% of platforms) |
| Fragile | violet → amber → red | Breaks after 3 bounces; colour warns you. (~25%) |
| Moving | green | Slides along a glowing rail. (~25%) |
About 40% of rows are intentionally empty, and ~5% of populated rows spawn a second platform alongside the first. Your best score is stored locally by the extension and shown on the game-over screen.
Architecture
The extension host is tiny: it opens a webview and persists the high score. All the real work happens in the browser-side rendering pipeline, written in TypeScript and bundled with esbuild.
src/
├── extension.ts Slim entry — registers the command.
├── host/
│ ├── gamePanel.ts Creates the webview panel and wires messages.
│ ├── webviewHtml.ts HTML template + CSP nonce.
│ └── highScore.ts Local high-score persistence.
├── shared/
│ └── protocol.ts Host ↔ webview message types.
└── webview/
├── main.ts Boot, game loop, reset/end game wiring.
├── types.ts Shared game + render interfaces.
├── gl/ WebGL2 toolkit (context, programs, textures, FBOs, batcher).
├── shaders/ GLSL ES 3.00 sources (.glsl files).
├── render/ Scene → bloom → composite pipeline.
└── game/ Config, platforms, physics, effects, input, view-model.
media/game.css Overlay UI (title, prompts, score, glitch effect).
out/ Build output (git-ignored, not committed).
├── extension.js Extension host bundle (esbuild, CJS).
└── webview/webview.js Webview bundle (esbuild, IIFE).
Rendering pipeline (per frame)
- Scene pass → an HDR float framebuffer.
- Procedural synthwave background (grid, sun, stars).
- A dark "backing" pass so each entity carves a readable outline out of the bright background, then an additive neon pass for emission.
- Bright pass — extract pixels above the bloom threshold.
- Blur — separable Gaussian, horizontal/vertical ping-pong for N iterations.
- Composite — scene + bloom, exposure & filmic tone mapping, then CRT curvature, chromatic aberration, scanlines, vignette, grain and bounce-flash, straight to the screen.
Develop
npm install # install dev dependencies
npm run watch # incremental esbuild (host + webview, F5 default build task)
npm run compile # type-check both projects + one-shot esbuild
npm run check-types # TypeScript only, no emit
The compiled output lives in out/ and is intentionally not committed — it
is regenerated by npm run compile and by vscode:prepublish before packaging.
License
MIT © Jan Kepinski