This is a submission for the June Solstice Game Jam
What I Built
Solstice is a puzzle game where you place mirrors to guide light beams across a grid. Each level has two modes — SUN and MOON. You toggle between them to route both beams to their targets. Both need to be lit at the same time to win.
Click a cell to place a /
mirror, click again for \
, again to remove. Right-click removes it. Ctrl+Z to undo. That's the whole mechanic — but the levels get hard fast.
There are 18 levels split into 7 tiers. It starts simple (one mirror, one beam) and builds up to puzzles where you have fewer than 10 free cells and every placement has to count. I verified every level with a Python solver to make sure they're all solvable at the stated par.
The game has a terminal with a Bletchley Park theme. You get 3 questions, the AI responds like a trapped wartime consciousness, and at the end you decide if it was human or machine. It connects to the Gemini API if you have a key, otherwise it uses pre-written responses.
VIDEO DEMO
{https://youtu.be/gqtylwILuJ8}
Code
A light-routing puzzle game with a custom ray-tracing engine, dual-spectrum beam physics, 18 levels across 7 tiers, a CRT terminal narrative, undo/redo, and a star-based rating system.
Built with vanilla JavaScript, rendered on HTML Canvas 2D, with audio synthesis via the Web Audio API and optional AI-powered dialogue through Google Gemini. Deployed as a static site on the Vercel CDN.
Each subsystem is a standalone ES module communicating through a central game state, orchestrated by a requestAnimationFrame
game loop.
flowchart LR
subgraph Pipeline[Core Pipeline]
direction LR
GL["GAME LOOP<br>main.js<br>requestAnimationFrame<br>renderer.draw()<br>updateUI()"]
RT["RAY TRACER<br>raytracer.js<br>Dual-pass simulation<br>SUN + MOON paths<br>Cycle detection"]
RD["RENDERER<br>renderer.js<br>3-pass canvas draw<br>Glow + dashes + pulses<br>Particle integration"]
PS["PARTICLES<br>particles.js<br>emit() / burst()<br>update(dt) / draw()<br>Lifecycle management"]
end
GL --> RT --> RD --> PS
style GL fill:#1a1a3a,stroke:#ffd700,color:#ffd700
style RT fill:#1a1a3a,stroke:#4a90d9,color:#4a90d9
style RD fill:#1a1a3a,stroke:#7b68ee,color:#7b68ee
style PS fill:#1a1a3a,stroke:#00ff88,color:#00ff88
style Pipeline fill:#0a0a1a,stroke:#333,color:#888
flowchart TB
subgraph Systems[Supporting Systems]
direction TB
IM["INPUT<br>input.js<br>Mouse +
…Play it here: https://solstice-game-pi.vercel.app
HOW I BUILT IT
Vanilla JavaScript with HTML Canvas for rendering. No frameworks, no libraries — just ES modules and the Canvas 2D API.
The ray tracer uses grid-based ray marching. Each frame runs twice — once for SUN, once for MOON. Mirrors transform the direction vector: /
does [dx, dy] → [-dy, -dx]
, \
does [dx, dy] → [dy, dx]
. Cycle detection prevents infinite loops from mirror pairs facing each other.
Audio is all Web Audio API oscillators — no sound files at all. The terminal uses the Gemini API when configured, with a system prompt that keeps the Bletchley persona consistent.
Levels are hand-designed with increasing wall density. Levels 11-18 went through multiple redesigns after my Python solver kept finding unintended shortcuts.
Prize Category
BEST ODE TO ALAN TURING ;
The terminal is a Turing Test. You talk to something that believes it was a code-breaker at Bletchley Park in 1945. Three questions, then you judge. The dialogue touches on Enigma, ration books, the hum of valves. The ending asks the same question Turing posed — can you tell the difference?
Best Google AI Usage
The terminal connects to the Gemini API for dynamic responses. The AI stays in character as a wartime consciousness. Without a key it falls back to pre-written dialogue, but with Gemini every conversation is unique.