An Overview of Algan’s Internals

Algan is a lazy animation system coupled to a batched 3-D raytrace renderer. Authoring code records changes rather than evaluating every frame immediately. Rendering later materializes the owning Scene’s timeline at a batch of frame times, converts the resulting mob states into render primitives, and sends those primitives through the raytracer to be rendered.

Scene containment

The central architectural unit is Scene. A Scene owns:

These managers are regular instances. They are recreated when that Scene is reset. SceneManager is the only singleton and has a narrower job: maintain the stack of active Scenes. Its current_scene is used only when authoring code omits an explicit owner.

A new Animatable resolves its Scene once in Animatable.__init__. Its ID, timeline rows, lifespan, and animation manager all come from that Scene. Existing mobs never consult the global active Scene for subsequent animation operations.

The animation system

Every Scene’s AnimationTimeline contains shared attribute buffers. A mob owns rows in each relevant buffer, keyed by its Scene-local ID. Attribute modifications record edit history for those rows; animated-function calls record function-application events; spawn and despawn operations record the mob’s lifespan.

AnimationContext objects control event timing. Seq, Sync, Lag, and Off are not global contexts: each belongs to one Scene’s AnimationManager. Implicit contexts use the active Scene manager, while methods on initialized Animatables bind their owning manager through a context-variable override.

A context cannot combine managers from multiple Scenes. Hierarchy and Group mutations enforce the same ownership boundary.

At materialization time, AnimationTimeline.set_state_to_times(times) reconstructs every attribute buffer for the requested frame times, replays animated functions with interpolated arguments, and runs active updaters. Materialization is batched according to the animation-memory budget in SETTINGS.computing.

Rendering entry points

The public APIs are save_frame() and save_video().

save_frame resolves the output path, optionally installs temporary video settings, materializes one or more timestamps, writes PNG images, and restores all derived render state. It does not reset the Scene.

save_video temporarily activates the target Scene and binds its AnimationManager before delegating to _render_scene_to_file, which carries the implementation while Scene.save_video carries the user-facing signature and documentation. It renders audio, streams video frames, and returns a RenderResult. Preflight failures and overwrite=False skips are observational and preserve authored state.

By default (reset=False) the Scene is left exactly as authored, so mobs stay valid and the timeline can keep growing. Two pieces of finalization are therefore conditional:

  • the end-of-scene despawn of every actor runs when a fade-out was requested, or when reset=True;

  • _render_to_video closes the camera and light lifespans only when the Scene is being finalized (despawn_camera_and_lights).

Both lifespans extend past the last rendered frame index either way, so output is unaffected by the choice. With reset=True the Scene’s timeline, animation and audio managers are rebuilt in a finally block on both success and failure, and mobs created before the render must not be reused.

Frame batches and scene assembly

RenderLoopMixin.get_frames divides the Scene timeline into windows sized by animation and rendering memory. For each window it materializes actors, builds render primitives, snapshots camera/light/environment state, and prepares the next batch concurrently unless prefetching is disabled.

Scene assembly merges each geometry class into contiguous tensor arrays and builds acceleration structures spanning the batch’s frame interval. Render out-of-memory errors reduce the frame window and retry rather than permanently changing public video settings.

Renderer paths

The primary entry point is render_batch_raytraced. The renderer selects a path from the live SETTINGS.raytracing configuration and scene features:

  • the deterministic wavefront path for single-sample rendering and features such as deterministic reflection/refraction;

  • the wavefront path tracer for multi-sample stochastic rendering where its capability set is sufficient;

  • the hybrid primary-raster path when enabled, with continuation rays handled by the wavefront machinery.

Renderer kernels and helper functions live in *_taichi.py modules. Keep those filenames because lint tooling excludes them from transformations that can break Taichi compilation.

Settings lifecycle

SETTINGS contains runtime-adjustable sections with stable object identity. Mutable sections update in place through set; immutable presets return modified copies. Initialization-only device and kernel-layout choices live in algan.settings._startup and are selected through environment variables before importing Algan.

Internal code should read settings through SETTINGS at use time. Do not capture mutable settings fields into module-level value imports unless the value is intentionally fixed for the process lifetime.