ScryingGlass — Display Engine Internals¶
ScryingGlass is a Python/Pyglet OpenGL application that renders a scene described by a JSON config file onto a fullscreen window. It runs as a separate process launched by the Rust backend (or manually), and watches its config file for changes — when you save a scene edit in the browser, the display updates automatically.
How It Fits In¶
Browser edits JSON → Rust saves to storage/scrying_glasses/
↓
watchdog detects file change
↓
display_engine_pyglet.py reloads & rerenders
One ScryingGlass process = one screen. Launch multiple processes (with different JSON files) for multiple player screens.
File Structure¶
| File | Purpose |
|---|---|
display_engine_pyglet.py |
Main engine (~1100 lines) — renders all element types |
fog_manager_pyglet.py |
Fog of war system (~485 lines) |
Rendering Pipeline¶
Pyglet uses OpenGL under the hood. All rendering goes through a batch — Pyglet draws everything in the batch in one GPU call per frame.
Frame loop: 1. Clear screen 2. Draw background (image or video — stays fixed, doesn't zoom/pan) 3. Apply zoom/pan transformation (modify GL projection matrix) 4. Draw all scene elements via batch (sprites, shapes, text) 5. Apply fog overlay via stencil buffer 6. Flip
Pyglet pushes alpha blending to the GPU — transparent 1080p video runs at 60 FPS with ~10% CPU usage.
Element Types¶
All elements are defined in the JSON config. The engine reads the type field and constructs the appropriate Pyglet object:
| Type | Pyglet object | Notes |
|---|---|---|
| Image | pyglet.sprite.Sprite |
PNG/JPG, supports opacity and rotation |
| Video | pyglet.media.Player + Sprite |
WebM (transparent), MP4; hardware decoded |
| GIF | pyglet.image.Animation |
Native frame timing |
| Text | pyglet.text.Label |
TTF/system fonts, hex colour, alignment |
| Token | pyglet.shapes.Circle + Arc border |
Circular marker with configurable colour |
| Area | pyglet.shapes.Rectangle / Circle |
Transparent fill, colour, opacity |
| Line | pyglet.shapes.Line |
Thickness as positional arg |
| Cone | Custom polygon | Sector shape for spell effects |
Positions and sizes accept absolute pixels, percentage strings ("50%"), or relative floats (0.5).
Zoom and Pan¶
Zoom and pan work by modifying the OpenGL projection matrix (glOrtho) rather than moving each element individually. This means:
- Zero CPU cost — it's a single matrix on the GPU
- All batched elements transform together automatically
- The background is drawn before the transform is applied, so it stays fixed
Controls: mouse wheel to zoom at cursor, right-click drag to pan, +/- keys, H/Home to reset. Zoom range is 0.1× to 10×.
Fog clearing is done in world space (before the transform) so it stays accurate when zoomed.
Fog of War (fog_manager_pyglet.py)¶
The fog is a coloured overlay that covers the whole scene. Players click to "clear" areas, revealing what's underneath.
How clearing works:
1. Click coordinates are converted from screen space → world space
2. A circle polygon is subtracted from the fog shape using pyclipper (falls back to simple ops if not installed)
3. The resulting cleared polygons are stored in the JSON config and persisted across reloads
Wall shadows: If walls are defined in the config, the fog manager casts shadow polygons behind them using line-of-sight geometry. Areas behind walls stay fogged even if the surrounding area is cleared.
Rendering: The fog uses the OpenGL stencil buffer — cleared polygons are written to the stencil, and the fog overlay is only drawn where the stencil is not set. This gives pixel-perfect transparency with no texture overhead.
Keyboard shortcut: C clears all fog. R manually reloads the config.
Live Config Reload¶
watchdog monitors the JSON file. On any file change event:
- All current elements are cleaned up (videos stopped, sprites deleted, batches cleared)
- JSON is re-read
- All elements are recreated from scratch
This happens in under a second and is what makes the browser editor feel live.
Dependencies¶
| Package | Purpose |
|---|---|
pyglet 2.0+ |
OpenGL window and rendering |
watchdog |
File system monitoring |
pyclipper |
Precise polygon subtraction for fog (optional) |
All are installed in the project's python-env.