Skip to content

Add execution timeline capture and JSON export - #5006

Open
behackl wants to merge 5 commits into
refactor/manager-no-rasterfrom
refactor/timeline-export
Open

behackl wants to merge 5 commits into
refactor/manager-no-rasterfrom
refactor/timeline-export

Conversation

@behackl

@behackl behackl commented Sep 8, 2026

Copy link
Copy Markdown
Member

Overview: What does this pull request change?

Stacked on #5005. Last one for the file writer refactor cycle!

Adds execution timeline capture to scene evaluation, with an immutable Python report and JSON export:

manager = scene.manager or Manager(scene)
manager.evaluate(capture_timeline=True)
timeline = manager.timeline
timeline.write("timeline.json")

The CLI can evaluate a selected scene and save its timeline directly:

manim --fps 4 --timeline-output timeline.json example_scenes/timeline_scene.py TimelineExample

The format and Python API are experimental and may change without a deprecation period.

Motivation and Explanation: Why and how do your changes improve the library?

A scene's timing depends on Python control flow, updaters, stop conditions, and frame-rate sampling. Capturing observations from the existing evaluation loop gives external tools a structured account of the execution for inspection and visualization.

The report includes:

  • Play and wait calls, with observed start/end times, requested durations, and top-level animation summaries.
  • Animation-step counts and frame intervals occupied by frozen waits.
  • Section, caption, and sound declarations, including call order and placement times.
  • Source-file and line hints, with occurrence counters for repeated calls at the same site.

Capturing, saving, and reading

manager.timeline becomes available after successful capture. to_dict() returns an independent dictionary, to_json() returns canonical JSON, and write() saves the captured report. Timeline.from_json() checks the schema name, version, completion flag, and content revision.

Version 1 uses the schema name manim.execution-timeline and a SHA-256 revision identifying the report's content.

The CLI resolves the output path before scene code runs. It compiles captured primary-file bytes and checks that file again during loading, evaluation, and cleanup. After those steps succeed, it writes a temporary JSON file and replaces the destination. If evaluation fails before publication, an existing report remains available as its original result.

Source hashes and freshness checks cover the primary file. Python capture reads the scene class's source file from disk before evaluation; CLI capture compiles the bytes it records. The standalone reader compares the current primary file with its recorded hash and marks changes as stale.

Capture also validates event completion, declaration serialization, and finite, nondecreasing animation time. Errors leave the capture incomplete so callers can fix the cause and evaluate a fresh scene.

Links to added or changed documentation pages

  • New timeline guide: docs/source/guides/timeline.rst.
  • Updated evaluation guide and Manager/Timeline API documentation.
  • Added example_scenes/timeline_scene.py and examples/timeline_reader.py. The reader uses Python's standard library to print events and optionally create an HTML view with source links and declarations.

@behackl behackl added the new feature Enhancement specifically adding a new feature (feature request should be used for issues instead) label Sep 8, 2026
Comment thread manim/timeline.py
frame = frame.f_back
return None
finally:
del frame
Comment thread manim/utils/module_ops.py


def get_module(file_name: Path) -> types.ModuleType:
def get_module(file_name: Path, *, source: bytes | None = None) -> types.ModuleType:

@nikolajmunk nikolajmunk left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some quick documentation suggestions. This overall reads very well and I'm excited about this experimental feature!

Standalone reader
-----------------

The repository also includes a reader that uses only Python's standard library:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suspect the user doesn't need to know that the reader uses only stdlib. Maybe remove?

Comment on lines +79 to +97
* ``policy`` is ``no-raster-full``, identifying the evaluation mode used for
capture. ``complete`` is true for a completed capture. ``termination`` is
``completed`` for ordinary completion, or ``scene-end-request`` when
``construct()`` ends with a handled ``EndSceneEarlyException``.
* Each event represents one play or wait call. It includes an ID, play index
(``ordinal``), kind, requested duration (``nominal_duration``), observed
``start`` and ``end`` times in seconds, and top-level animation types/run times.
* ``samples`` counts animation steps; ``hold_intervals`` counts the frame
intervals occupied by a frozen wait. In the Python example these are
``(2, 0)`` and ``(0, 1)``. When a stop condition ends a wait early, ``end``
records the time reached by that wait.
* ``declarations`` contains section, caption, and sound calls. Their ``at`` time
records when the call happened; placement can differ because of an offset.
Captions have resolved ``start`` and ``end`` times. Sounds have a ``start``;
``duration`` is null to indicate an unknown duration. Relative sound paths
retain the requested string. Sound options must be JSON-serializable.
* Events and declarations share an ``order`` counter. A declaration's
``event_id`` refers to a play being prepared or executed, or is null outside
one. ``event_boundary`` is the number of completed plays when it was declared.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would turn this into a more formal list with descriptions of every key-value in the report, largely for the sake of future maintainability.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While this is technically a correct example, I question the utility of including it in the example gallery when none of its visual output is actually relevant to the concept that the example is explaining.

I would either remove this entirely or find a way to turn it into a visual example. Maybe there's a way to fetch the timeline info of one scene and actually display it as a visual timeline in another scene?

Comment thread manim/manager.py
Comment on lines +346 to +349
"""Return the completed timeline requested during :meth:`evaluate`.

Read this property after ``evaluate(capture_timeline=True)`` returns
successfully. The returned snapshot is immutable.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
"""Return the completed timeline requested during :meth:`evaluate`.
Read this property after ``evaluate(capture_timeline=True)`` returns
successfully. The returned snapshot is immutable.
"""The completed timeline produced by a successful execution of
:meth:`evaluate` with argument ``capture_timeline=True``. This timeline
is immutable.

Perhaps something like this?

@behackl
behackl force-pushed the refactor/timeline-export branch from 5e94c10 to 296331a Compare September 17, 2026 15:27
@behackl
behackl force-pushed the refactor/timeline-export branch from 296331a to 6af115a Compare September 17, 2026 17:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

new feature Enhancement specifically adding a new feature (feature request should be used for issues instead)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants