session

The viewer’s state: one Scene, one render worker, one lock.

Everything the browser asks for goes through a ViewerSession. It owns the Scene being inspected and serialises every touch of it, because two things about Algan’s renderer make concurrent access wrong rather than merely slow:

  • A render binds Taichi’s arch and allocates the render arena for its runtime, so two renders at once fight over both.

  • Reading an attribute “at time t” materializes the Scene’s whole timeline at that time. It is global mutation, undone afterwards – so a render sharing the Scene would read the wrong state.

So there is one worker thread that renders, one lock that every Scene access takes, and HTTP handler threads that wait on results rather than computing them. What that lock must not do is stand between the page and answers it could have had without touching the Scene at all, which is what the next paragraph is about.

There are two locks, not one, and the split is the difference between a viewer that answers and one that does not. _scene_lock guards the Scene itself and is held for as long as a render or a materialized read takes. _lock guards only this object’s own bookkeeping – the frame cache, the playhead, the error – and is never held across anything slow. Routes that need no Scene access (/api/state, a cached /frame/N.png) therefore answer immediately even while a chunk is rendering.

The worker is also the lowest-priority user of the Scene. Python locks are not fair: a worker that releases the Scene lock at the end of a chunk and re-takes it at the top of the next one wins that race against a request that has been waiting since before the chunk started, and can keep winning for the whole video. So a request announces itself in _scene_demand before it queues, and the worker stands aside while that count is non-zero and abandons the chunk it is in at the next batch boundary. A request waits for the batch already in flight, not for the rest of the video.

Frames are rendered lazily, a chunk at a time, and cached as encoded PNGs. A seek does not cancel the chunk already running – get_frames is a generator and abandoning it mid-batch would waste the batch’s materialization – but it does redirect what the worker renders next, so the wait is bounded by one chunk rather than by the rest of the video.

Classes

ViewerSession

A Scene, rendered on demand for a browser to page through.