360 Dish Lab

The isolated prototype for a third game camera β€” 🌎 3rd person and πŸ‘ 1st person meet a new πŸͺ¬ 360 POV rendered by dash-pannellum 0.3.0. The sphere is a live canvas the lab redraws from the specimen's position, so swimming moves the world; an auto-pilot directs the camera like a nature documentary (framing and following focus subjects, with camera emotes on impacts, deaths and respawns), and manual mode steers gaze-relative from a joystick.

360 Dish Lab

The isolated prototype for a third game camera β€” 🌎 3rd person and πŸ‘ 1st person meet a new πŸͺ¬ 360 POV rendered by dash-pannellum 0.3.0. The sphere is a live canvas the lab redraws from the specimen's position, so swimming moves the world; an auto-pilot directs the camera like a nature documentary (framing and following focus subjects, with camera emotes on impacts, deaths and respawns), and manual mode steers gaze-relative from a joystick.

---

.. toc::

A third point of view, isolated

The game already has two cameras: 🌎 3rd person (the observer's dish, scroll/drag) and πŸ‘ 1st person (the specimen's eye, rendered by CV.renderArena with the eyespot tier deciding what you see). This lab prototypes the third β€” πŸͺ¬ 360: standing *inside* the dish at the specimen's eye height, looking around a full sphere. Like the [Tileset Dish Lab](/tileset-dish), it is deliberately isolated from /petri-dish-evolution so the POV can get polished before it earns a place in the game.

.. exec::docs.pano_dish.pano_dish

Movement for real: the dynamic canvas

[dash-pannellum](https://pypi.org/project/dash-pannellum/) 0.3.0 lets a tour scene bind to a live <canvas> (panoramaCanvasId + dynamicUpdate) β€” redraw the canvas and the sphere updates in place: *no rebuild, no flash, no camera reset*. The lab's renderer (assets/pano_dish.js, window.P360R) composes the dish interior from the specimen's position every other tick:

independent art: the condenser light, bokeh plankton, light shafts);

arena360 ground-ray technique, at half resolution) β€” your membrane ring and shadow ride along at the nadir;

so the meniscus glow line rises as you approach the glass and recedes as you leave;

brightfield = classic silhouettes), with a pulse on threats and a focus ring on the camera's subject.

Swim and the whole world moves. Illumination (πŸŒ‘/πŸ’‘) is now just a palette swap inside the renderer β€” the viewer is never rebuilt after boot, which is the three-tier design the package docs preach: *look* every frame (lookAt/drag), *move* at game cadence (redraw the canvas), *world* rarely (re-output tour).

The documentary director

The jitter fix is structural, not cosmetic β€” both the body and the camera move on slow signals:

*subjects* and *escape vectors* β€” never a per-tick random heading;

deadbands) whose duration grows with the angle β€” constant angular speed reads like a held camera, and Pannellum's own easing does the in-between;

the camera frames it (hfov eases 95 β†’ 78), follows it as both drift, and the pilot circles rather than bowls it over. Click any contact in the sphere to make it the focus yourself; threats steal focus with a wider, warier shot.

Camera emotes (assets/camera_emotes.js β€” the dash-pannellum quick sheet of keyframed lookAt timelines) punctuate the loop: a *flinch* on wall bumps and threat contact, a decaying *shake* when the daphnia closes in, a *knockdown* (slam β†’ stunned beat β†’ slow rise toward the swim heading) on exhaustion, a waking *scan* on respawn, an hfov *lunge* punch when the pilot commits to a subject. The director yields while an emote plays (CAM_EMOTES.isBusy()), so reflexes never fight the framing.

Manual: the gaze is the steering frame

πŸ•Ή Manual arms the joystick (and WASD). In the top-down POVs input is world-absolute, but in the 360 view it is gaze-relative β€” stick up = swim where you're looking β€” which is the only mapping that feels right from inside a sphere. The pannellum zoom chrome is hidden (showZoomCtrl: false rides the tour config into the viewer; scroll / pinch / keyboard zoom all still work), and the compass docks under the joystick where a pilot's instruments belong.

Coordinates: one bearing contract

World x = east, y = south (SVG convention). A contact's yaw is atan2(dx, βˆ’dy), so yaw 0Β° = north = "up" on the dish map, clockwise β€” the placement.js contract the adaptation wedges use. Pitch comes from the floor geometry: a contact at distance *d* sits at βˆ’atan(eye/d) β€” exactly where the live floor puts it, so sprites stand on the agar. rc-joystick reports math-convention angles (0Β° = right, counter-clockwise); bearing = 90 βˆ’ angle.

Tilesets: the multiRes dish

The second viewer serves the ray-traced dish as a Pannellum multires pyramid β€” six cube faces cut from an 8192Γ—4096 master with the exact libpannellum face conventions, tiled into 126 tiles across 3 levels (cube 2048, tile 512, plus a 1024 fallback per face), streamed level-by-level as you zoom:

``python multiRes={ "basePath": "/assets/pano/multires/dish", "path": "/%l/%s%y_%x", "fallbackPath": "/fallback/%s", "extension": "jpg", "tileResolution": 512, "maxLevel": 3, "cubeResolution": 2048, } ``

Scorecard against the 0.1.0 wishlist

This lab's first build ran turn-based purely because 0.1.0 had no imperative API, and its wishlist drove the next two releases:

1. ~~Imperative hotspots~~ β€” shipped 0.2.0 (callbackHotspots diffs in place). The lab uses them as sparse click-zones; the *visual* contacts moved into the canvas texture, which suits per-tick motion even better. 2. ~~Camera writes~~ β€” shipped 0.2.0 (lookAt, plus hfov reporting). The director and the emote sheet are built on it. 3. ~~Canvas/data-URI panorama sources~~ β€” shipped 0.3.0 (panoramaCanvasId + dynamicUpdate). Position moves through the world now. 4. Hotspots + viewer config in multiRes mode β€” still open (the tiled dish above remains config-only). 5. Bundled (non-CDN) runtime β€” still open.

---

*Source: /360-dish*

Note for AI agents: This is the static, prerendered view of an interactive Dash application served because we detected a non-JS user agent. Full prose docs: