A projection-mapped chemistry game where physical black blocks on a board, seen by a camera, become invisible physics walls for virtual compound particles.
🇹🇷 Türkçe özet: KimyaLab, dik bir tahtaya projeksiyonla yansıtılan fiziksel-dijital melez bir kimya laboratuvarı oyunudur; tahtaya konan siyah bloklar kamerayla tespit edilip oyunda görünmez fizik duvarına dönüşür. Çevrimdışı çalışan, lisans kapılı, Nuitka + Inno Setup ile paketlenen bir sergi yazılımıdır; kurulum ve kalibrasyon ayrıntıları docs/INSTALL.md içindedir.
Game field (design reference, version 13): six pipes with valves, one per compound, and the beaker at the bottom center.
KimyaLab is an exhibition piece, not a desktop game. A 16:9 game field is projected onto an upright board. Six pipes (H₂O, Mg, Na, O₂, HCl, K) extend in from the edges and drip colored compound particles. Visitors place solid black blocks on the board; the camera finds them and the game treats each one as an invisible wall or ramp, so particles bounce off the real block as if it were part of the simulation. When different compounds collect in the beaker, a reaction animation plays and a score is awarded. Valves open while a black disc is held over them.
The blocks are never drawn in the projection. The physical world and the physics engine are overlaid on each other, which is why calibrating the camera-to-projection mapping is the core engineering problem of the project.
It runs fully offline on a Windows 11 exhibition PC and opens in its own full-screen embedded window; no browser, no address bar. Gameplay details, the reaction table, physical dimensions and hardware requirements are in docs/PROJE-RAPORU.md (Turkish).
- Camera-driven physical interaction. Black blocks are detected by image processing only: no markers, no QR codes.
- Pluggable obstacle sources. The game engine never knows where obstacles come from; it only calls
ObstacleSource.poll() -> list[Obstacle]. A fixed-bar source runs without a camera for testing, a contour source reads the camera in production. - In-game calibration wizard. Camera, lens, thresholds, measurements, an 8-point geometry mapping and live fine-tuning in one operator UI, with no terminal needed.
- Offline by design. The physics engine and fonts are vendored; there are no network requests at runtime.
- License-gated distribution. The game assets ship encrypted and cannot be opened without a signed license.
- Windows installer. Nuitka standalone build wrapped in an Inno Setup installer with a SHA-256-stamped ZIP.
+---------------------------+
camera --> OpenCV --> | obstacle_source.py |
(black blocks) | ObstacleSource.poll() | FakeObstacleSource
| Contour / Fake sources | (fixed bars, no camera)
+-------------+-------------+
| Obstacle list
+-------------v-------------+
| ws_server.py | WebSocket bridge,
| ws://127.0.0.1:8765, 30 Hz| operator monitor window
+-------------+-------------+
| JSON "state" messages (PROTOCOL.md)
+-------------v-------------+
| web/ client | Matter.js physics,
| canvas2d, full screen | pipes, valves, beaker, reactions
+---------------------------+
web_calistir.py starts HTTP (web_sunucu.py), the WS bridge and the embedded window.
The Python side does vision and the browser side does the game. They are coupled only by the versioned WebSocket data contract in PROTOCOL.md (currently v6, additive changes only). Ports 8000/8765 are picked automatically if occupied and injected into the page.
| Layer | Technology |
|---|---|
| Vision | Python 3.11+, OpenCV 4.x (Otsu + contours + minAreaRect, homography), NumPy |
| Transport | websockets (WS bridge), built-in HTTP server |
| Game client | JavaScript, Matter.js (vendored), Canvas 2D |
| Window | pywebview (embedded full-screen WebView) |
| Licensing | Ed25519 signatures, AES-256-GCM content package (cryptography) |
| Packaging | Nuitka standalone, Inno Setup 6 |
| Tests | pytest, camera-free |
- Calibration you can trust. The camera-to-game mapping is an 8-point homography (4 corners + 4 edge midpoints) reported with a residual in pixels. The same 8 crosshair positions exist in both Python (
kalibrasyon.py) and the browser (web/render.js);tests/test_nisangah_esitligi.pyfails if they ever diverge, because silent drift would make calibration look successful while shifting every block. - Order is enforced in code. Lens calibration must precede geometry; if the lens is recalibrated afterward, the geometry is flagged stale and the wizard asks for it again (
contour_calibration.geometri_bayat_mi). The wizard is a state machine (sihirbaz.py) tested without a camera. - Graceful degradation. If the camera cannot be opened the app still starts without it and points the operator at the camera step of the wizard, instead of exiting and making the fix unreachable.
- Stable tracking. Detected blocks go through alpha-beta tracking with coasting, so a block briefly occluded by a hand keeps its identity.
- Protocol regression guard. With the wizard closed, the
statemessage keys must stay identical to the previous protocol version;tests/test_ws_sihirbaz.pypins the key set, and it is extended deliberately, in the same commit, when the protocol grows. - Offline-safe by test.
tests/test_cevrimdisi.pyfails if a CDN link leaks back in; an earlier CDN dependency produced a blank screen when the exhibition had no internet. - Cryptographic license gate, not an
if. The game'sweb/tree is obfuscated, zipped and AES-256-GCM encrypted intoweb.pak. The decryption key is not in the executable; it travels in the signed license payload. Only an Ed25519 public key is embedded, so the app can verify a key but never mint one. Activation is bound to the machine (2 of 3 hardware identifiers) and guards against clock rollback. - Packaging lessons captured in tests. A onefile build triggered a Windows Defender ML false positive, so the project moved to a Nuitka standalone folder inside an Inno installer;
tests/test_kurucu.pyblocks onefile from returning and blocks local calibration or activation files from leaking into the installer. - Path handling for frozen builds. All file paths go through one module (
paths.py) that distinguishes read-only resources from writable data, so calibration is never written to a temporary extraction folder;tests/test_yollar.pylocks this. - Test suite. 625 test functions across 41 files, none requiring hardware. Camera polling, the GUI calibration loops and projection alignment are verified by hand on the real setup.
Version 0.9.1 (Nuitka standalone build and Inno Setup installer are produced; see docs/TESLIM.md for the delivery flow). Calibration wizard, publish preparation and the license gate are complete. The code-signing certificate has not been acquired, so Windows shows a SmartScreen warning on first install. A legacy 4-corner calibration path is retained because its homography loader is still used in the live pipeline. The earlier pygame desktop version is archived under docs/arsiv/ and unused. Formerly named SEKTIRMECE.
| Path | Purpose |
|---|---|
web_calistir.py |
Single-command launcher (HTTP + WS + window) |
ws_server.py |
Vision-to-browser WebSocket bridge and operator monitor |
obstacle_source.py, obstacle_geometry.py |
Obstacle sources (contour / fake) and segment/polygon conversion |
kamera_geometri.py, ince_ayar.py |
Camera-to-game math and fine-tune persistence |
sihirbaz.py |
Calibration wizard state machine |
web/ |
Browser client (Matter.js physics, rendering, pipes, beaker, reactions) |
PROTOCOL.md |
Python ↔ browser WebSocket data contract |
lisans*.py, web_paket.py |
License gate and encrypted content package |
build.py, installer/ |
Nuitka build and Inno Setup installer |
tests/ |
Camera-free pytest suite |
docs/ |
Project report, install guide, delivery notes, plans and specs |
python -m venv .venv && source .venv/bin/activate # Windows: .\.venv\Scripts\activate
pip install -r requirements.txt
python web_calistir.py # no camera: fixed test bars
python web_calistir.py --contour # with camera (production mode)Run the tests with pip install -r requirements-dev.txt && pytest. The production target is Windows 11; a KIMYALAB.bat launcher is provided for source runs.
Full setup (flags, packaging, installer, calibration, data folders, licensing): docs/INSTALL.md (Turkish).
