Repository navigation
Highlights 🌟
Unified output selection and configurable video encoding
Choosing what Manim produces has been reworked around a single format
setting. --format (or format in the [CLI] section of a config file)
selects exactly one primary artifact: auto (the default; MP4, or MOV for
transparent output), mp4, mov, webm, gif, png (last frame only),
png-sequence (every frame), or none (evaluate the scene without writing
media). It replaces the overlapping --write_to_movie, -g/--save_pngs,
-i/--save_as_gif, and --force_window options. Viewing the result is now
separate from choosing what to write: -p/--preview opens the finished file
with either renderer, while -l/--live-preview shows a live window (OpenGL).
As a consequence, the OpenGL renderer now writes files by default, just like
Cairo. The --custom_folders preset has been removed in favor of setting
each output location separately. (#4966, #4969)
Video encoding is now configurable, too: the new [video_encoder] config
section and the --video-codec, --pixel-format, and repeatable
--encoder-option KEY=VALUE options control how partial movie files are
encoded, for example
manim render --video-codec libx264 --encoder-option crf=18 --encoder-option preset=slow scene.py.
The new manim cache clear command replaces the --flush_cache flag. (#4976)
Scenes rendered with default settings need no changes, but scripts and config
files relying on the removed options have to be updated. See the migration
notes in the Breaking Changes section below.
Comprehensive documentation for the updated configuration system
can be found in the
configuration guide and the
output settings tutorial.
A Manager for coordinating the render flow
This release takes a first step towards a full overhaul of Manim's
render lifecycle by introducing a Manager object that coordinates
the processing of a Scene. Regular scenes (i.e., those without
custom cameras, renderers, or file writers) do
not need any changes.
Until now, the responsibilities of the render lifecycle were spread
across Scene, the renderer, the camera, and the file writer.
As the name suggests, the Manager is now the central manager of this process.
This release lays the foundation for the new architecture. The render
loop has not been fully restructured yet.
Upcoming releases will build on this to move rendering execution
and file writing into the Manager.
This will allow us to remove the remaining cross-dependencies between
scenes, renderers, and file writing.
The rendering deep dive has been updated to
describe the new flow. (#4916)
Improved rendering of Text etc. with small font size
This release also features a new version of our plain text rendering
engine (Pango) wrapper ManimPango that fixes a long-standing issue with the kerning
(spacing between letters) for text with very small font sizes. Now
mobjects like Text("hello world, this is a test", font_size=1) look crisp
and are typeset as intended. (#5041)
What's Changed
Breaking Changes 🚨
-
Introduce a
Managerto coordinate the render flow by @behackl in #4916Scenes, including overrides of
setup,construct, andtear_down, need no
changes.Scene.render,Scene.play,Scene.next_section,
Scene.add_subcaption, andScene.add_soundnow delegate to the scene's
Manager(scene.manager), which owns the render lifecycle formerly
implemented inScene.render. Code that reimplemented or patched these
internals should call the publicScenemethods instead of driving the
renderer or file writer directly. The "Rendered … Played N animations" log
record now originates from themanagermodule instead ofscene. -
Rework output configuration options and how they are consumed by @behackl in #4966
Output is now selected with the single
formatoption; the old switches have
been removed:--write_to_movie/write_to_movie = True: use the default
format = autoor an explicitmp4,mov,webm, orgif;
write_to_movie = Falsebecomesformat = none.-g/--save_pngs:--format png-sequence(frames are written to
media/images/<module>/<Scene>/).-i/--save_as_gif:--format gif.save_last_framein config files:format = png. The-sflag and
config.save_last_framestill work.--force_window:-l/--live-preview; add a concrete format to also
record the live render.-f:--show_in_file_browser.config.movie_file_extension:config.format.
Behavior changes:
--format pngsaves only the last frame; an explicit video
format fails for scenes withoutplayorwaitcalls (autosaves a PNG
instead); with OpenGL,-popens the finished file instead of a live window
and files are written by default; transparent output requiresauto,mov,
orwebm. Output settings are captured when aSceneis constructed, so
create scenes inside the intendedtempconfigblock. Rendering no longer
overwritesconfig.output_file; read
scene.renderer.file_writer.final_file_pathinstead. The format helpers
is_mp4_format,is_gif_format,is_png_format,is_webm_format,
is_mov_format, andwrite_to_moviewere removed from
manim.utils.file_ops; inspectscene.session_specor
scene.manager.output_specinstead. For custom renderers and file writers,
see #4976. -
Refactor media output path resolution and remove the
--custom_foldersCLI flag and config option by @behackl in #4969--custom_folders,config.custom_folders, and the[custom_folders]config
section have been removed; set the regular directory options under[CLI]
instead. The former preset corresponds to:[CLI] media_dir = videos video_dir = {media_dir} sections_dir = {media_dir} images_dir = {media_dir} text_dir = {media_dir}/temp_files tex_dir = {media_dir}/temp_files log_dir = {media_dir}/temp_files partial_movie_dir = {media_dir}/partial_movie_files/{scene_name}
-o/--output_fileis now rejected when rendering more than one scene
(with-aor several scene names); drop it or render scenes separately. The
resolved format determines the file suffix, so-o movie.mov --format mp4
producesmovie.mov.mp4. Section videos now include the section name (e.g.
MyScene_0000_create-square.mp4); read filenames from thevideofield of
the section JSON instead of constructing them. -
Move video segment encoding to a dedicated module and detach
SceneFileWriterfrom renderer state by @behackl in #4976Replace
--flush_cacheandflush_cache = Truewith
manim cache clear scene.py SceneName. The[ffmpeg]config section is now
[media](loglevelstays the same), andconfig.ffmpeg_loglevelis now
config.media_loglevel. Cache keys now include the encoder settings, so
existing cached segments are re-rendered once.SceneFileWriter.clean_cache()
andflush_cache_directory()were replaced byprune_segment_cache()and
clear_segment_cache()inmanim.utils.caching.Custom renderers and file writers:
init_scenenow has the signature
init_scene(scene, session_spec, file_writer_settings), and the writer is
constructed from these settings alone (it no longer receives the renderer or
reads the globalconfig). Renderers must declarecapabilities. Frames are
passed as owneduint8RGBA arrays of shape(height, width, 4)with
top-left origin:write_frame(pixels, repeat=n)andsave_image(pixels);
begin_animation()andopen_partial_movie_stream()additionally require
animation_index=.get_hash_from_play_call()requires thebackend,
encoder_fingerprint, andrenderer_statekeyword arguments, and
get_full_sound_file_path()requiresassets_dir.
Enhancements 🚀
- Improve the error message of
throw_error_if_no_pointsand raiseValueErrorinstead ofExceptionby @smmariquit in #4927 - Add a
keyparameter tolist_updateandlist_difference_updateto support unhashable elements by @nikolajmunk in #4975
Bug Fixes 🐛
- Fix OpenGL-specific code paths that were skipped because renderer comparisons against the string
"opengl"never matched by @pjfo in #4929 - Fix mobject duplication during animations by treating ancestors of moving mobjects as moving by @nikolajmunk in #4936
- Allow
GraphandDiGraphto be initialized without vertices by @christopher-hampson in #4946 - Fix singular/plural form of "submobject(s)" in
VGroup.__str__by @nikolajmunk in #4950 - Fix right-bounds check in
OpenGLMobject.is_off_screenby @GniLudio in #4953 - Fix
OpenGLMobject.stretch_to_fit_depthstretching along the wrong dimension by @GniLudio in #4955 - Fix
OpenGLMobject.get_boundary_pointby @GniLudio in #4956 - Restore renderer class bases when updating the config from another
ManimConfigby @emerardd in #4958 - Skip hidden files and tolerate vanishing files when cleaning the partial movie file cache by @changsheng0804-blip in #4954
- Fix
LaggedStartMapunpacking submobjects when using the defaultarg_creatorby @SAY-5 in #4965 - Replace deprecated
np.sumcalls on generators, fixing aTypeErrorwith the OpenGL renderer by @christopher-hampson in #4962 - Preserve the layout of
MathTypstcapture groups by @behackl in #4960 - Fix
normalizeraising an error instead of returning the fallback vector for zero-norm vectors by @nikolajmunk in #5001 - Fix
is_closedmisclassifying closed curves in negative coordinate space by @Osamaali313 in #5010 - Fix
angle_axis_from_quaternionreturning the inverse rotation for angles greater than π by @dylanpulver in #4980 - Fix
LineJointType.AUTOandCapStyleType.AUTOinheriting the previous mobject's style in the Cairo renderer by @RizgarOzan in #5016 - Fix
BarChartignoringinclude_numbersiny_axis_configby @RizgarOzan in #5015 - Fix graph label placement and coordinate conversion on logarithmic axes by @GreedyC in #5039
- Fix SVG shapes with
stroke="none"getting a white outline by @RizgarOzan in #5036 - Fix
colorargument ofTextandMarkupTextnot being applied to the mobject itself by @webzuweb in #5017 - Fix the
redirectionsparameter of thedeprecated_paramsdecorator by @GniLudio in #5028 - Fix rendering WebM videos with sound when PyAV has no libvorbis encoder by @Darkslayer3324j in #5025
- Fix
ThreeDScene.set_camera_orientationwith the OpenGL renderer by @Smail in #5040 - Fix
Mobject.insertandOpenGLMobject.replace_submobjectpermitting duplicate submobjects by @nikolajmunk in #4957
Performance ⚡
- Speed up
list_updateandlist_difference_updateby using a set for membership checks by @nikolajmunk in #4939 - Speed up redundancy removal in
get_familyand fix families containing loops by @GniLudio in #4947 - Improve speed and robustness of
MobjectandOpenGLMobjectadd/remove operations by @nikolajmunk and @GniLudio in #4957 - Bound the encoder frame queue when rendering without parallel encoding to limit memory usage by @pizzaburgare in #5049
Documentation 📚
- Fix flaky
ConvexHullexample in the rendered documentation by @behackl in #4967 - Mention ruff as formatter and linter in the documentation by @nikolajmunk in #4978
- Add guidance on installing the C++ Build Tools on Windows by @SR0725 in #4982
- Add missing module reference for Typst mobjects by @behackl in #5007
- Add instructions for the optional Typst installation by @aryansk in #4986
- Update the testing guide by @cqxswbc2 in #4991
- Improve the documentation of
TangentialArcby @henrikmidtiby in #5046
Testing 🧪
- Add tests verifying that updaters affect their mobjects by @nikolajmunk in #5000
Type Hints 📝
- Use
ParamSpecin deprecation decorators to preserve signatures of decorated callables by @GniLudio in #4988 - Add type hints to
vector_field.pyby @henrikmidtiby in #5030
Dependencies 📦
- Update ManimPango dependency to version 0.7.0 by @naveen521kk in #5041
- Update Tornado, Jupyter, and virtualenv dependencies by @behackl in #5047
Other Changes
New Contributors
- @emerardd made their first contribution in #4958
- @smmariquit made their first contribution in #4927
- @SAY-5 made their first contribution in #4965
- @SR0725 made their first contribution in #4982
- @aryansk made their first contribution in #4986
- @Osamaali313 made their first contribution in #5010
- @dylanpulver made their first contribution in #4980
- @cqxswbc2 made their first contribution in #4991
- @RizgarOzan made their first contribution in #5016
- @GreedyC made their first contribution in #5039
- @webzuweb made their first contribution in #5017
- @Darkslayer3324j made their first contribution in #5025
- @Smail made their first contribution in #5040
- @pizzaburgare made their first contribution in #5049
Full Changelog: Compare view