Skip to main content

manim-radar

Animated radar (spider) charts for Manim — build one, morph it into another, or play a whole sequence with a single command.

PyPI Python License: MIT CI

manim-radar 0.1.1 showcase

from manim import *
from manim_radar import RadarChart, DeepSpace, set_scene_background

AXES = ["Speed", "Power", "Range", "Comfort", "Price", "Safety"]

class Demo(Scene):
    def construct(self):
        set_scene_background("midnight")
        chart = RadarChart(axes=AXES, values=[8, 6, 7, 9, 5, 8], title="Six axes")

        self.add(DeepSpace().link(chart))              # optional starfield
        self.play(chart.reveal())                      # grow out of the centre
        self.play(chart.morph_to([5, 9, 5, 6, 9, 9],   # one command = one morph
                                 color="#FFB74D"))
        self.play(chart.disappear())
manim -qh demo.py Demo          # 1080p60

Install

pip install manim_radar

Requires Python ≥ 3.9, manim >= 0.18 and numpy. pip pulls those in for you.

Features

One command morphing chart.morph_to(...) tweens values, full scale, colour, emphasis outline and opacity in a single pass
Seven transition presets smooth, dip, shockwave, zoom, glitch, stepped, burst (new in 0.1.1) — plus every knob to fine-tune them
Multi-dataset comparison datasets=[dA, dB]: several polygons share one grid, morph and reel together (new in 0.1.1)
Overflow radar OverflowRadarChart: values above the full scale stab beyond the ring — dashed track, breach glow, true read-out (new in 0.1.1)
Camera language chart.push_in() / pull_out(), or a true-camera CameraRig for MovingCameraScene (new in 0.1.1)
One command sequences RadarReel(chart, snapshots).play_on(self) plays a whole reel, black gaps included
3 styles × 5 themes neo (glow / rainbow outline / sweep beam), classic (the lean 1.0 look), minimal (light themes, reports)
Odometer numbers digits roll continuously instead of snapping, with automatic 10+ overflow labels
Real Manim mobjects the chart is a VGroup: shift / move_to / scale / rotate / FadeIn / FadeOut all behave
Deep-space backdrop DeepSpace adds a radial glow, corner vignette, nebula blobs and drifting stars that tint with the chart
Two generations included RadarV1Scene and RadarV2Scene rebuild the original 1.0 and 2.0 reels from the library
Cross-platform fonts picks the first installed CJK / monospace font, works on macOS, Linux and Windows

Demos

Every GIF below was rendered straight from the matching example scene (manim -ql + ffmpeg); they all live in docs/.

0.1.1 showcase 0.1.1 feature showcase — multi-dataset comparison, overflow spikes and camera moves. examples/10_showcase.py
quickstart Quickstart — build → reveal → morph → fade, in ten lines. examples/01_quickstart.py
transitions Transitions — smooth / shockwave / zoom / glitch / rim. examples/02_morph_gallery.py
reel One-command reel — a whole sequence, black gap included. examples/03_reel.py
styles and themes 3 styles × 5 themes — from neo glow to a light paper report. examples/04_styles_themes.py
classic vs neo The two generations — classic (1.0) vs neo (2.0), same data. examples/05_two_styles.py

New in 0.1.1

Multi-dataset comparison

A 0.1.0 chart carried a single dataset; 0.1.1 charts can compare two inequalities — two game builds, two models — on one grid:

holder = RadarData(axes=AXES, values=[8, 9, 10, 4, 9, 7], name="Hölder",
                   color="#17B8BA", max_value=10)
basic  = RadarData(axes=AXES, values=[10, 6, 5, 10, 7, 9], name="AM–GM",
                   color="#E868C8", max_value=10)

chart = RadarChart(datasets=[holder, basic], radius=2.4)
self.play(chart.reveal())

# morph every dataset at once (stagger = per-dataset wave delay)
self.play(chart.morph_to([holder_next, basic_next], stagger=0.15))

chart.focus(0)              # highlight dataset 0, dim the rest
chart.highlight_gap(0, 1)   # tint the crescent gap, each lobe in the winner's colour
chart.clear_gap()
  • Comparisons draw a legend (colour chip + name pills, auto-wrapped).
  • Per-axis numbers have four modes via compare_numbers=: "legend" (default — cleanest), "all" (small coloured numbers at every vertex), "first", "focus" (follows chart.focus(i)).
  • The full scale is the largest of the snapshots', so all datasets read from the same ruler.
  • RadarReel accepts comparison steps — pass a list of RadarData per step and the reel morphs them together.

OverflowRadarChart (the boundary-breaking chart)

Values above the full scale are no longer clamped to "10+" — they stab beyond the outer ring:

from manim_radar import OverflowRadarChart

chart = OverflowRadarChart(axes=AXES, values=[8, 6, 14, 9, 5, 8],
                           max_value=10, name="Berserker", color="#F040E0")
self.play(chart.burst_in())                     # stab out of the centre
self.play(chart.morph_to([7, 9, 17, 6, 8, 5]))  # burst transition by default
  • overflowing axes grow a dashed track; the polygon stays filled;
  • a glowing breach marks where the spike pierces the ring;
  • overflow read-outs show the true value (14, 17…) riding the spike;
  • transition="burst": values gather low, then stab past the target and settle back — plain charts can use it too;
  • comparisons overflow as well: OverflowRadarChart(datasets=[dA, dB], max_value=10) — every dataset gets its own spikes, tracks, breaches and read-outs (see examples/10_showcase.py, the promo reel).

Camera language

# plain Scene: the chart scales about its own centre
self.play(chart.push_in(factor=1.35, run_time=0.8))
self.play(chart.pull_out(factor=1.35, run_time=0.8))

# MovingCameraScene: a true camera (the starfield breathes too)
from manim_radar import CameraRig

class MyScene(MovingCameraScene):
    def construct(self):
        rig = CameraRig(self)
        self.play(rig.push_in(chart, factor=1.4))
        self.play(rig.focus_point(peak_vertex, factor=1.6))
        self.play(rig.pull_out())
        chart.attach_camera(rig)   # burst morphs now auto-push towards the spike

See examples/07_compare.py, examples/08_overflow.py, examples/09_camera.py.

The three objects

from manim_radar import RadarChart, RadarData, RadarReel

RadarData — one snapshot: the axes and the value on each of them.

RadarData(
    axes=["A", "B", "C", "D", "E", "F"],
    values=[8, 6, 7, 9, 5, 8],
    name="Model A",        # optional nameplate (morphed on every transition)
    color="#17B8BA",       # optional main colour (else the theme palette cycles)
    max_value=10,          # optional full scale; above it values render as "10+"
    emphasis="rim",        # "zoom" | "gap" | "rim" — used by RadarReel
    hold=0.6,              # seconds to hold on this snapshot (RadarReel)
    transition="glitch",   # per snapshot transition override
)

RadarChart — the mobject.

chart = RadarChart(data_or_axes=..., style="neo", theme="midnight", radius=3.2)

chart.set_values([1, 2, 3, 4, 5, 6])   # instant, no animation
chart.set_main_color("#E868C8")
chart.set_rim(1.0)                     # orange/cyan emphasis outline
chart.set_radius(2.4)
chart.attach_backdrop(self)            # shortcut for DeepSpace().link(chart)

self.play(chart.reveal())              # intro
self.play(chart.morph_to(other))       # the money shot
self.play(chart.glitch_in())           # glitchy re-entrance
self.play(chart.disappear())           # outro

RadarReel — the whole sequence in one line.

RadarReel(
    chart,
    snapshots=[d1, d2, d3, d4],   # list[RadarData]
    hold=0.6,                     # default hold per snapshot
    transition="shockwave",       # default transition
    backdrop=True,                # starfield, auto-tinted
    gap_hold=1.5,                 # length of a snapshot's "gap" black-out
).play_on(self)

Transitions

transition= What it does
None (default) dip below the target, then roll up (the signature neo move)
"smooth" straight tween, no dip
"shockwave" dip + an expanding ring that covers the shape change
"zoom" shockwave + zoom & dim, then snap back (the "big transition")
"glitch" chromatic split + flicker while the values change
"stepped" values snap in integer steps (the classic feel)
self.play(chart.morph_to(target, transition="zoom", run_time=1.6))

Styles and themes

chart = RadarChart(data, style="neo",       theme="midnight")
RadarChart(data, style="classic")           # the 1.0 look
RadarChart(data, style="minimal", theme="paper")   # light background
  • Styles change structure: neo, classic, minimal (see STYLES).
  • Themes change colours: midnight, aurora, ember, violet, paper.

Everything is a dataclass, so you can start from a preset and tweak:

from manim_radar import RadarChart, RadarConfig, RadarTheme

cfg = RadarConfig(radius=2.4, levels=4, numbers="swap", sweep=False, rainbow_edges=False)
theme = RadarTheme(name="mine", grid="#8899AA", axis_colors=("#5EEAD4", "#F0ABFC") * 3)
chart = RadarChart(data, config=cfg, theme=theme)

RadarConfig() has ~60 documented fields: geometry, grid, polygon layers, fonts and sizes, decorations, transition timings, title/nameplate placement. RadarTheme holds every colour (grid, per-axis colours, palette, rims, star accents…).

Reuse inside a bigger scene

Reveal a chart, then morph it while you write something else beside it:

chart = RadarChart(AXES_DATA, radius=2.2)
chart.move_to(LEFT * 3.4)
self.play(chart.reveal())
self.play(chart.morph_to(next_data), Write(my_formula))

Because the chart is a plain VGroup whose geometry is recomputed in its own coordinate frame every frame, moving or scaling it never breaks the drawing.

Backdrop

from manim_radar import DeepSpace, set_scene_background

set_scene_background("midnight")           # flat background colour of the theme
backdrop = DeepSpace(theme="midnight", star_count=80, nebula_count=8)
backdrop.link(chart)                       # nebula / halos follow the chart colour
self.add(backdrop)

The two generations

Both original looks ship as ready-to-run scenes, built on the same library:

manim -qh examples/legacy_v1.py RadarV1Scene   # 1.0 : lean grid, snap-in numbers
manim -qh examples/legacy_v2.py RadarV2Scene   # 2.0 : glow, rainbow outline, shockwaves

They play the bundled 23-snapshot demo reel (manim_radar.legacy.REPLICA_TIMELINE).

CLI

manim-radar --version
manim-radar themes                  # list colour themes
manim-radar styles                  # list style presets
manim-radar template my_chart.py    # write a starter scene
manim-radar demo --render           # render the bundled demo reel

Rendering

manim -ql scene.py MyScene      # 480p15, fast preview
manim -qh scene.py MyScene      # 1080p60, final

If you are driving Manim from another tool, pass --media_dir to keep the render cache out of your source tree.

Publishing notes

The package is a standard PEP 621 project using hatchling:

python -m build            # -> dist/manim_radar-<version>-py3-none-any.whl + .tar.gz
twine check dist/*         # optional sanity check
twine upload dist/*        # or use the Release workflow (PyPI Trusted Publishing)

The distribution is registered as a Manim plugin (manim.plugins entry point), so manim plugins lists it once installed.

License

MIT — see LICENSE.


Also by the same author: manim-handdraw (turn line art into progressive hand-drawn animation).

Release files for manim-radar 0.1.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for manim-radar 0.1.1
File Size Uploaded
manim_radar-0.1.1.tar.gz 71.0 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for manim-radar 0.1.1
File Interpreter ABI Platform
manim_radar-0.1.1-py3-none-any.whl Python 3 none any Details

Total release size: 128.1 kB

Release files / manim_radar-0.1.1.tar.gz

Download URL manim_radar-0.1.1.tar.gz
Size 71.0 kB
Tags Source
SHA-256 checksum
How to use checksums
8d47f3a83eb4117e79e7f36193dad8e4092f0432117baf137fc795fee62cb57c
BLAKE2b-256 checksum
How to use checksums
3ebaba939e3ef29226abd9dcc71e7cbb85578e4256179710d3c96b99a2061613
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 25, 2026.

Transparency log

Release files / manim_radar-0.1.1-py3-none-any.whl

Download URL manim_radar-0.1.1-py3-none-any.whl
Size 57.1 kB
Tags Python 3
SHA-256 checksum
How to use checksums
2b5fcfd9cb5af29a50768a0efbb5ada1aae1cf465a58446dba2477457a854de7
BLAKE2b-256 checksum
How to use checksums
9df6534a83a30f8712519168e08d18ae4b1352276030597bfe20dc9026bee486
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 25, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.1.1 This release

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page