Documentation

Python scene-writing docs for Manim Web

Learn the authoring model, follow actual Manim constraints, and copy known-good patterns that compile cleanly in Manim Web.

Actual Manim semanticsGuides for scene authorsNarrative-first scene design
GuideMedia And Audio9 sections

Audio And Sound Effects

Choose the correct scene-timeline audio channel, mix voiceover, background audio, and secure bundled effects into one MP4, and browse every available sound ID.

Choose An Audio Channel

Audio is classified by purpose and timing. Pick the channel first; the renderer keeps its metadata separate in the editor and combines all active channels into the final MP4 mix.

Use caseAPITimingEditor color
Spoken narration and captionswith self.play_voiceover_block(...)Starts at block entry and can pad scene time to the narration duration.Sky blue
Music, ambience, or a continuous bedwith self.play_background_audio_block(...)Covers exactly the with-block span; supports looping, volume, and fades.Violet
A built-in click, impact, ambience, music track, or other packaged cuewith self.play_sound("category/name")Starts at block entry and, by default, is bounded by the with-body.Emerald green
One final audio stream
These are authoring and timeline channels, not three browser audio players. The renderer mixes them with any enabled VideoMobject audio and writes the combined stream into the MP4. The timeline retains the separate cue types so timing remains understandable.

Voiceovers

Use a voiceover scene class when spoken narration should own pacing or captions. Define stable keys withset_voiceovers, then play a key in a context. The tracker exposes the measuredduration, and the scene is padded when narration outlasts the animations in its block.

from manim import * class NarratedOrbitScene(VoiceoverThreeDScene):    def construct(self):        self.set_voiceovers({            "intro": {                "text": "Reusable narration on top of a Three D scene.",                "mp3": "/data/audio/intro.mp3",            },        })         sphere = Sphere(radius=1.2)        with self.play_voiceover_block("intro") as tracker:            self.play(Create(sphere), run_time=min(1.0, tracker.duration)) 

Choose VoiceoverScene, VoiceoverMovingCameraScene, orVoiceoverThreeDScene to match the visual scene type. Upload or generate narration first and bind the resulting workspace path. Do not use bundled sound effects for speech.

Background Audio

Use background audio for music or ambience that should cover a known scene span. It works on base and voiceover scene classes. The source is an uploaded workspace asset or workspace-confined audio path.

from manim import * class BackgroundAudioScene(Scene):    def construct(self):        with self.play_background_audio_block(            "/data/audio/theme.mp3",            volume=0.25,            fade_in=0.2,            fade_out=0.3,            loop=True,        ):            self.play(Create(Circle()), run_time=2)            self.wait(1) 

loop=True repeats the source to cover the block. Without looping, playback stops when the source ends. A leading slash means the root of the current user workspace—not the server filesystem. Targets outside that workspace and symlink escapes are rejected.

Sound Effects

Use self.play_sound for a trusted, package-owned sound, including both short effects and the longer documentary tracks below. It works on Scene, ThreeDScene, moving-camera scenes, and every voiceover variant. It does not accept uploaded files, paths, URLs, or raw audio data—only an exact catalog ID.

from manim import * class SoundEffectScene(Scene):    def construct(self):        button = RoundedRectangle(width=3.2, height=1.2)        label = Text("Launch").move_to(button)        self.add(button, label)         # Without duration, the with-body is the playback boundary. A longer        # sound is trimmed; a shorter sound ends naturally.        with self.play_sound("ui/click", volume=0.8) as cue:            self.play(button.animate.set_fill(BLUE, opacity=0.35), run_time=0.4)         with self.play_sound("movement/slide", fade_out=0.05):            self.play(VGroup(button, label).animate.shift(UP * 2), run_time=0.8)         # duration requests an audible span. Extending beyond the source        # requires loop=True.        with self.play_sound(            "ui/click",            duration=1.2,            loop=True,            fade_in=0.15,            fade_out=0.25,        ):            self.play(button.animate.set_stroke(YELLOW), run_time=1.2)         # The sound can continue over later scene work without padding this        # block when asynchronous timing is intentional.        with self.play_sound("ui/confirm", duration=0.5, pad_block=False):            self.wait(0.02) 

Without an explicit duration, the with-body is authoritative: a longer source is trimmed, a shorter source ends naturally, and loop=True repeats a shorter source through the body. This default never adds a sound-driven wait. An explicit duration sets the requested playback span and may outlast the body. If it also outlasts the source, only loop=True repeats the source; otherwise playback stops at the source's natural end with no fake silent tail. pad_block=True waits for explicit playback that actually outlasts the body, while pad_block=False lets it continue asynchronously.

durationloopPlayback durationBlock behavior
OmittedFalseSource or with-body, whichever ends firstNever adds a sound-driven wait.
OmittedTrueExact with-body durationTrims or repeats to the body end; adds no wait.
Explicit, within sourceEitherRequested durationPads only when playback outlasts the body.
Explicit, beyond sourceFalseNatural source durationStops naturally; no silent tail is reported.
Explicit, beyond sourceTrueRequested durationRepeats from the beginning to the exact duration.

Nature effects

from manim import * class NatureSoundScene(Scene):    def construct(self):        feather = TablerIcon("feather", color=YELLOW).scale(1.2)        drop = Dot(color=BLUE).shift(UP * 2)         with self.play_sound("nature/wind/gust", volume=0.7):            self.play(FadeIn(feather), run_time=0.5)         with self.play_sound("nature/water/drop"):            self.play(drop.animate.shift(DOWN * 4), run_time=0.7)         with self.play_sound("nature/weather/thunder", fade_in=0.05):            self.play(Flash(ORIGIN, color=WHITE), run_time=0.4) 
Rain, rivers, and forest ambience
Sustained user-owned environmental beds belong to the background-audio channel, where loop=Trueconstrains them to the block. Use an uploaded rain asset with play_background_audio_block when the bundled catalog does not contain the recording you need.

This nature example is also kept as the runnable renderer fixturepackages/manim_renderer/tests/fixtures/nature_sound_effect_scene.py so the documented API is exercised by real MP4 verification.

ArgumentContract
nameRequired exact lowercase hierarchical ID such as ui/click or impact/light.
volumeFinite value from 0 through 4; defaults to 1.
fade_in / fade_outEach is 0 through 10 seconds and cannot exceed playback duration. They may overlap; fades apply once at the overall beginning and end.
durationOptional finite value greater than 0 and at most 3600 seconds. When omitted, the with-body is the playback boundary.
loopDefaults to False. Repeats only when the body or explicit duration extends beyond the source. Restarting is exact but does not crossfade a non-loop-ready source.
pad_blockDefaults to True. Waits only when explicit playback actually outlasts the with-body. False lets following scene work begin while audio continues.
context resultsource_duration is fixed; playback_duration/final_duration, body_duration, and block_duration expose the resolved timing.

Unknown well-formed IDs produce a compile error with close-name suggestions. Malformed input—including an extension, whitespace, traversal segment, URL, markup, shell fragment, non-string value, or string subclass—is rejected before FFmpeg runs. Without an explicit duration, playback depends on the completed with-body, so fade fit is validated when that body exits.

Audio output formats
MP4 exports contain the mixed audio. GIF and PNG cannot contain audio, although sound blocks can still affect scene timing. Overlapping loud tracks are peak-limited in the final mix to prevent clipping. Preview and export MP4 files also include Manim Web provenance tags: build and Manim versions, preview/export mode, selected render profile, scene class, resolution, frame rate, creation time, and the Manim Web URL.

Documentary Library

The documentary/* namespace is a built-in CC0 collection for serious science, space, technology, and reflective storytelling. Use it only through play_sound. The separate background-audio API continues to resolve user-owned workspace audio and does not expose these packaged files.

from manim import * class ComputationDocumentary(Scene):    def construct(self):        title = Text("What Is Computation?")         # With no duration, this longer built-in bed ends exactly with the body.        with self.play_sound(            "documentary/music/serious-ambient",            volume=0.25,            fade_in=1.0,            fade_out=2.0,        ):            self.play(Write(title), run_time=2)            self.wait(3)         with self.play_sound("documentary/transition/whoosh", volume=0.35):            self.play(title.animate.scale(0.85).to_edge(UP), run_time=0.6)         with self.play_sound("documentary/impact/low-cinematic-boom", volume=0.3):            self.play(FadeIn(Text("Universe → Information → Computation"))) 
Core collectionVocabularyUse it for
Existing Kenney + 100 CC0 SFX #2 bundleDigital, UI, glitches, impacts, sci-fi, low-frequency explosionsComputer, transistor, information, and transition details
Owlish Media Sound Effects Pack161 ambience, cloth, footsteps, human, impact, paper, sci-fi, technology, UI, and water recordingsEarth, laboratories, hardware, interfaces, and documentary foley
50 CC0 Sci-Fi SFX50 beeps, machines, terminal cues, loops, teleports, and unusual electronic texturesComputation, computers, data, and technology atmosphere
Documentary music collectionTen space, mystery, reflection, and slow-tension tracks from Joth, yd, Ruskerdax, and TinyWorldsUniverse, physics, entropy, intellectual curiosity, and philosophical pacing
Cinematic transition collectionTwo appliance drones, 13 swishes, a scientific sting, and a low boomRisers, whooshes, chapter transitions, reveals, and restrained emphasis

A short built-in documentary cue is also kept as the runnable renderer fixturepackages/manim_renderer/tests/fixtures/documentary_sound_effect_scene.py and is used for real MP4 audio verification.

Verified original sources

Every source below is the original uploader's download and license page. Each is marked CC0 1.0: commercial use, editing, looping, layering, remixing, worldwide distribution, and monetized-video use are permitted; attribution and payment are not required. Formats and byte sizes describe the exact pinned downloads used by the generator, not an unrelated preview.

OwlishMedia · CC0-1.0 · 161 assets · WAV · 135.8 MiB
Commercial: yes · attribution: none · modification/remixing: yes · YouTube monetization: yes · direct pinned archive
rubberduck · CC0-1.0 · 50 assets · OGG · 2.3 MiB
Commercial: yes · attribution: none · modification/remixing: yes · YouTube monetization: yes · direct pinned archive
artisticdude · CC0-1.0 · 13 assets · WAV · 376 KiB
Commercial: yes · attribution: none · modification/remixing: yes · YouTube monetization: yes · direct pinned archive
LEGIT Audio · CC0-1.0 · 2 assets · WAV · 18.5 MiB
Commercial: yes · attribution: none · modification/remixing: yes · YouTube monetization: yes · direct pinned archive
yd · CC0-1.0 · 1 asset · OGG · 4.0 MiB
Commercial: yes · attribution: none · modification/remixing: yes · YouTube monetization: yes · direct pinned archive
yd · CC0-1.0 · 1 asset · OGG · 768 KiB
Commercial: yes · attribution: none · modification/remixing: yes · YouTube monetization: yes · direct pinned archive
Joth · CC0-1.0 · 5 assets · MP3 · 5.4 MiB
Commercial: yes · attribution: none · modification/remixing: yes · YouTube monetization: yes
yd · CC0-1.0 · 1 asset · OGG · 3.7 MiB
Commercial: yes · attribution: none · modification/remixing: yes · YouTube monetization: yes
Ruskerdax · CC0-1.0 · 1 asset · MP3 · 11.7 MiB
Commercial: yes · attribution: none · modification/remixing: yes · YouTube monetization: yes
TinyWorlds · CC0-1.0 · 1 asset · MP3 · 2.9 MiB
Commercial: yes · attribution: none · modification/remixing: yes · YouTube monetization: yes
NenadSimic · CC0-1.0 · 1 asset · WAV · 886 KiB
Commercial: yes · attribution: none · modification/remixing: yes · YouTube monetization: yes
Fupi · CC0-1.0 · 1 asset · OGG · 197 KiB
Commercial: yes · attribution: none · modification/remixing: yes · YouTube monetization: yes
License ranking: A only
This built-in collection contains only rank-A CC0/public-domain assets. Rank-B attribution licenses and rank-C custom licenses are intentionally not bundled, even when a download is described as royalty-free.
CC0 permission and automated claims are different questions
CC0 gives the legal permissions above, and the catalog pins creator/uploader pages and exact bytes so a claim can be documented and disputed. No audio library can guarantee that a third party will never submit an erroneous Content ID claim. Keep the source link and catalog checksum from the manifest with the project.

Sound ID Taxonomy

There are two intentional naming layers. Canonical IDs retain the pinned source-pack namespace, making every upstream file addressable without a handwritten Python subset. Semantic aliases provide stable intent-based names for generated scene code.

LayerExamplesWhen to use it
Canonical pack IDsinterface/confirmation-001, general/door-01, impact/footstep-grass-000Use when selecting an exact variation from a complete source pack.
Curated nature IDsnature/bird/chirp, nature/water/drop, nature/wind/softUse real pinned CC0 environmental one-shots without uploading a file.
Documentary canonical IDsdocumentary/music/yd/out-there, documentary/rubberduck-sci-fi/terminal-01Use an exact music, ambience, technology, foley, or transition asset from the verified documentary collection.
Documentary semantic aliasesdocumentary/music/serious-ambient, documentary/transition/whooshPrefer for durable generated documentary code when a stable intent-based default is enough.
Semantic aliasesui/confirm, nature/weather/thunder, movement/slide, impact/lightPrefer for durable generated code when one stable default is enough.
from manim import * print(sound_effect_count())       # 869 source assetsprint(len(sound_effect_names()))  # 899 names, including stable aliases for name in sound_effect_names():    if name.startswith("interface/"):        print(name) 
What “complete catalog” means
The bundle contains every audio file from seven pinned Kenney packs and the complete “100 CC0 SFX #2” pack, plus ten curated nature assets and 238 documentary assets: 869 canonical assets and 30 aliases for 899 selectable names. There is no smaller _SOUND_EFFECTS runtime dictionary. “Complete” means every real audio file in each named bundled pack is present; it does not claim to contain every sound published on the internet.

Combine All Three

Contexts may be nested. Each records its own start time and timing rule; the final mixer aligns all executed cues against the scene timeline.

from manim import * class ThreeAudioChannelsScene(VoiceoverScene):    def construct(self):        self.set_voiceovers({            "intro": {                "text": "Three independent audio channels, one final video.",                "mp3": "/data/audio/narration.ogg",            },        })        title = Text("Three audio channels")         with self.play_background_audio_block(            "/data/audio/theme.ogg",            volume=0.2,            fade_out=0.15,            loop=True,        ):            with self.play_voiceover_block("intro"):                with self.play_sound("ui/confirm", volume=0.7):                    self.play(Write(title), run_time=0.6)             with self.play_sound("impact/light"):                self.play(title.animate.scale(1.1), run_time=0.4) 

This exact pattern is kept as the runnable renderer fixturepackages/manim_renderer/tests/fixtures/three_audio_channels_scene.py. Replace its two workspace paths with uploaded narration and background files; bundled effect IDs need no upload.

Security, Packaging, And Performance

ConcernBehavior
Path and command injectionScene code supplies only a strict allowlisted ID. The final mixer resolves it again under the package-owned asset root and ignores scene-provided paths or URLs.
Source repository sizeGenerated audio files and catalog JSON are ignored by Git and excluded from the Docker source context. Only the generator, pinned checksums, provenance, runtime, tests, and docs are source.
InstallationThe first local build, test, or dev command fills the ignored cache automatically. Docker downloads assets in a dedicated cacheable stage, keeps a checksum-addressed BuildKit download cache across generator changes, and re-verifies bytes before reuse. Production needs no separate download.
Runtime compile costScenes without effects do not parse the catalog. The 899-name catalog is loaded once per worker process; later valid resolutions reuse the in-memory dictionary and still verify that the resolved package file exists.
Search spaceExact IDs use an O(1) dictionary lookup. The 899-name similarity scan runs only for an unknown ID and is never part of a valid render.

The generated catalog currently contains 266.7 MiB of original audio. Runtime compilation performs no network request. Archive checksums, direct-file checksums, per-file checksums, durations, count, aliases, and file existence are verified while generating the image.

Complete Sound Catalog

Filter by ID, source, or alias target. Click any body cell to copy that row's ID. The bounded table scrolls independently and keeps its header visible. It is generated at static-doc build time from the same checked manifest used by the Python renderer, so documentation and the container catalog stay aligned.

899 of 899 selectable IDs
Bundled sound catalog. Click any body cell to copy that row's sound ID.
Sound IDTypeResolves toSource
Page 1 of 9

Click any body cell to copy that row’s sound ID.