JAnim Documentation¶
JAnim is a library for creating smooth animations, built around the core concept of programmatic animation. It supports real-time editing, live preview, and a wide range of additional powerful features.
Below are some example showcases (Note: you can click on the tab titles to quickly collapse this section)
HelloJAnimExample ¶
from janim.imports import *
class HelloJAnimExample(Timeline):
def construct(self):
# define items
circle = Circle(color=BLUE)
square = Square(color=GREEN, fill_alpha=0.5)
# do animations
self.forward()
self.play(Create(circle))
self.play(Transform(circle, square))
self.play(Uncreate(square))
self.forward()
BasicAnimationExample ¶
from janim.imports import *
class BasicAnimationExample(Timeline):
def construct(self):
circle = Circle()
star = Star()
self.forward()
self.play(Create(circle))
self.play(circle.anim.points.shift(LEFT * 3).scale(1.5))
self.play(circle.anim.set(color=RED, fill_alpha=0.5))
self.play(SpinInFromNothing(star))
self.play(star.anim.points.shift(RIGHT * 3).scale(1.5))
self.play(star.anim.set(color=YELLOW, fill_alpha=0.5))
self.forward()
See also:
TextExample ¶
from janim.imports import *
class TextExample(Timeline):
def construct(self):
txt = Text('Here is some text', font_size=64)
desc = Group(
Text('You can also apply <c BLUE>styles</c> to the text.', format=Text.Format.RichText),
Text('You can also apply <c GREEN><fs 1.4>styles</fs></c> to the text.', format=Text.Format.RichText),
)
group = Group(txt, desc)
group.points.arrange(DOWN, buff=MED_LARGE_BUFF)
self.forward()
self.play(Write(txt))
self.play(FadeIn(desc[0], UP))
self.play(Transform(desc[0], desc[1]))
self.forward()
TypstExample ¶
from janim.imports import *
typst_doc = t_(
R'''
JAnim provides `TypstText` and `TypstMath` classes to insert Typst content.
Math expressions are also supported.
$ A = pi r^2 $
$ "area" = pi dot "radius"^2 $
$ cal(A) :=
{ x in RR | x "is natural" } $
#let x = 5
$ #x < 17 $
You can also use `TypstDoc`, which automatically align to the top of the viewport,
instead of the center.
''')
class TypstExample(Timeline):
CONFIG = Config(
typst_shared_preamble=t_(
R'''
#import "@janim/colors:0.0.0": *
#show raw: set text(BLUE)
''')
)
def construct(self):
doc = TypstDoc(typst_doc)
group = Group(
Text('TypstText', color=BLUE),
TypstText('This is a sentence with a math expression $f(x)=x^2$'),
Text('TypstMath', color=BLUE),
TypstMath('sum_(i=1)^n x_i = x_1 + x_2 + dots.c + x_n')
)
group.points.arrange_in_grid()
# The rendering speed of animations applied to text is relatively slow
self.play(Write(doc), duration=4)
self.forward()
self.play(FadeOut(doc))
self.play(Write(group))
self.forward()
self.play(FadeOut(group))
See also:
TypstColorizeExample ¶
from janim.imports import *
class TypstColorizeExample(Timeline):
def construct(self):
typ = TypstMath('cos^2 theta + sin^2 theta = 1', scale=3).show()
self.forward()
self.play(typ['cos'].anim.set(color=BLUE))
self.play(typ['sin'].anim.set(color=BLUE))
self.play(typ['theta', 0].anim.set(color=GOLD))
self.play(typ['theta', 1].anim.set(color=ORANGE))
self.forward()
self.play(typ['theta', ...].anim.set(color=GREEN))
self.play(typ['space^2', ...].anim.set(color=RED))
self.forward()
See also:
AnimatingPiExample ¶
from janim.imports import *
class AnimatingPiExample(Timeline):
def construct(self):
grid = TypstMath('pi') * 100
grid.points.scale(2).arrange_in_grid(10, 10, buff=0.2)
grid.show()
self.play(grid.anim.points.shift(LEFT))
self.play(grid(VItem).anim.color.set(YELLOW))
self.forward()
self.play(grid(VItem).anim.color.set(BLUE))
self.forward()
self.play(grid.anim.points.to_center().set_height(TAU - MED_SMALL_BUFF))
self.forward()
self.play(grid.anim.points.apply_complex_fn(np.exp), duration=5)
self.forward()
self.play(
grid.anim.points.apply_point_fn(
lambda p: [
p[0] + 0.5 * math.sin(p[1]),
p[1] + 0.5 * math.sin(p[0]),
p[2]
]
),
duration=5
)
self.forward()
NumberPlaneExample ¶
from janim.imports import *
class NumberPlaneExample(Timeline):
def construct(self):
plane = NumberPlane(faded_line_ratio=1)
sin_graph = plane.get_graph(lambda x: math.sin(x))
self.forward(0.2)
self.play(Write(plane, lag_ratio=0.05))
self.play(Write(sin_graph))
self.forward()
self.play(
plane.anim.points.apply_matrix([
[3, -1],
[1, 2]
]),
sin_graph.anim(),
duration=2
)
self.forward()
See also:
UpdaterExample ¶
from janim.imports import *
class UpdaterExample(Timeline):
def construct(self):
square = Square(fill_color=BLUE_E, fill_alpha=1).show()
brace = Brace(square, UP).show()
def text_updater(p: UpdaterParams):
cmpt = brace.current().points
return cmpt.create_text(f'Width = {cmpt.brace_length:.2f}')
self.prepare(
DataUpdater(
brace,
lambda data, p: data.points.match(square.current())
),
ItemUpdater(None, text_updater),
duration=10
)
self.forward()
self.play(square.anim.points.scale(2))
self.play(square.anim.points.scale(0.5))
self.play(square.anim.points.set_width(5, stretch=True))
w0 = square.points.box.width
self.play(
DataUpdater(
square,
lambda data, p: data.points.set_width(
w0 + 0.5 * w0 * math.sin(p.alpha * p.range.duration)
)
),
duration=5
)
self.forward()
See also:
ArrowPointingExample ¶
from janim.imports import *
class ArrowPointingExample(Timeline):
def construct(self):
dot1 = Dot(LEFT * 3)
dot2 = Dot()
arrow = Arrow(dot1, dot2, color=YELLOW)
self.show(dot1, dot2, arrow)
self.play(
dot2.update.points.rotate(TAU, about_point=RIGHT * 2),
GroupUpdater(
arrow,
lambda data, p:
data.set_start_and_end(
dot1.points.box.center,
dot2.current().points.box.center
)
),
duration=4
)
See also:
CombineUpdatersExample ¶
from janim.imports import *
class CombineUpdatersExample(Timeline):
def construct(self):
square = Square()
square.points.to_border(LEFT)
# Here, a new Updater is accumulated for every `play`
# to show the effect of animation combination
self.play(
square.anim.points.to_border(RIGHT),
duration=2
)
###############################
square.points.to_border(LEFT)
self.play(
square.anim.points.to_border(RIGHT),
DataUpdater(
square,
lambda data, p: data.points.shift(UP * math.sin(p.alpha * 4 * PI)),
become_at_end=False
),
duration=2
)
###############################
square.points.to_border(LEFT)
self.play(
square.anim.points.to_border(RIGHT),
DataUpdater(
square,
lambda data, p: data.points.shift(UP * math.sin(p.alpha * 4 * PI)),
become_at_end=False
),
square.update(become_at_end=False).color.set(BLUE).r.points.rotate(-TAU),
duration=2
)
See also:
RotatingPieExample ¶
from janim.imports import *
class RotatingPieExample(Timeline):
def construct(self):
pie = Group(*[
Sector(start_angle=i * TAU / 4, angle=TAU / 4, radius=1.5, color=color, fill_alpha=1, stroke_alpha=0)
.points.shift(rotate_vector(UR * 0.05, i * TAU / 4))
.r
for i, color in enumerate([RED, PURPLE, MAROON, GOLD])
])
self.play(
GroupUpdater(
pie,
lambda data, p: data.points.rotate(p.alpha * TAU, about_point=ORIGIN),
duration=5
),
DataUpdater(
pie[0],
lambda data, p: data.points.shift(normalize(data.mark.get()) * p.alpha),
rate_func=there_and_back,
become_at_end=False,
at=2,
duration=2
)
)
See also:
MarkedItemExample ¶
from janim.imports import *
class MarkedSquare(MarkedItem, Square):
def __init__(self, side_length: float = 2.0, **kwargs):
super().__init__(side_length, **kwargs)
self.mark.set_points([RIGHT * side_length / 4, DOWN * side_length / 4])
class MarkedItemExample(Timeline):
def construct(self):
square = MarkedSquare()
tri1 = Triangle(radius=0.2, color=GREEN)
tri2 = Triangle(radius=0.2, color=BLUE)
dots = DotCloud(color=RED)
self.play(
square.update.points.rotate(TAU),
DataUpdater(
square,
lambda data, p: data.points.shift(RIGHT * math.sin(4 * math.pi * p.alpha))
),
DataUpdater(
tri1,
lambda data, p: data.mark.set(square.current().mark.get())
),
DataUpdater(
tri2,
lambda data, p: data.mark.set(square.current().mark.get(index=1))
),
DataUpdater(
dots,
lambda data, p: data.points.set(square.current().mark.get_points()),
skip_null_items=False
),
duration=4
)
See also:
FrameEffectExample ¶
from janim.imports import *
class FrameEffectExample(Timeline):
def construct(self):
squares = Square(0.5, color=BLUE, fill_alpha=0.3) * 49
squares.points.arrange_in_grid()
effect1 = SimpleFrameEffect( # (2~8s) Squares at [::2] produce a color gradient
squares[::2],
shader='''
f_color = frame_texture(v_texcoord);
f_color.gb *= v_texcoord;
'''
)
effect2 = SimpleFrameEffect( # (4~8s) Squares at [1::2] produce a glitch effect
squares[1::2],
shader='''
vec2 uv = v_texcoord;
float glitchStrength = sin(time) * 0.02;
vec2 offset = vec2(glitchStrength, 0.0);
float r = frame_texture(uv + offset).r;
float g = frame_texture(uv).g;
float b = frame_texture(uv - offset).b;
float a = max(frame_texture(uv + offset).a, max(frame_texture(uv).a, frame_texture(uv - offset).a));
float lineNoise = step(0.5, fract(uv.y * 10.0 + time));
r *= lineNoise;
b *= lineNoise;
f_color = vec4(r, g, b, a);
''',
uniforms=['float time']
)
self.schedule(2, effect1.show)
self.play(
Rotate(squares, TAU, duration=8),
DataUpdater(
effect2,
lambda data, p: data.apply_uniforms(time=p.elapsed),
at=4,
duration=4
)
)
See also:
ThreeDShapesExample ¶
from janim.imports import *
class _ThreeDShapesExampleSub(Timeline):
def __init__(self, shape_type: str, background_color: JAnimColor):
super().__init__()
self.shape_type = shape_type
self.background_color = background_color
def construct(self):
if self.shape_type == 'smooth':
axes = ThreeDAxes((-8, 8), (-8, 8), (-8, 8)).apply_depth_test()
self.prepare(FadeIn(axes, at=1))
background = FrameRect(fill_alpha=1, fill_color=self.background_color, stroke_alpha=0, depth=100)
background.fix_in_frame()
self.prepare(FadeIn(background, at=1.5, duration=3))
for shape in [Torus(2, 1), Cylinder(2, 4), Cone(2, 4)]:
item = shape.into(self.shape_type).show()
self.play(self.RotatingCamera(), duration=4)
item.hide()
def RotatingCamera(self):
return AnimGroup(
DataUpdater(
self.camera,
lambda data, p: data.points.rotate(TAU * p.alpha, axis=RIGHT),
rate_func=linear
),
DataUpdater(
self.camera,
lambda data, p: data.points.rotate(TAU * p.alpha, axis=OUT),
rate_func=linear
),
)
class ThreeDShapesExample(Timeline):
def construct(self):
subs = [
_ThreeDShapesExampleSub(type, color).build().to_item()
for type, color in zip(
['checker', 'wire', 'smooth', 'dots'],
['#000022', '#000033', '#000033', '#000022']
)
]
subs[0].show()
effects = [
RectClip(sub, anchor=ORIGIN)
for sub in subs
]
effects[0].show().depth.set(-1)
dirs = [UL, UR, DL, DR]
self.forward()
for sub, effect, dir in zip(subs, effects, dirs):
self.show(sub, effect)
self.prepare(
effect.anim
.points.scale(0.5).to_border(dir, buff=0)
.r.transform.set(scale=0.5)
)
self.forward_to(subs[0].duration)
MaskExample ¶
from janim.imports import *
class MaskExample(Timeline):
def construct(self):
## Part 1: Text floating up animation in a rectangular mask
txt = Text("Mask Example!")
txt.points.scale(2).move_to(DOWN * 0.5)
# Create a rectangular mask positioned above the text
mask1_shape = Rect(txt.points.box.width, txt.points.box.height)
mask1_shape.points.next_to(txt, UP, buff=0.5)
mask1 = ShapeMask(txt, shape=mask1_shape).show()
# Text gradually floating upward
self.play(
*[
char.anim.points.shift(UP * (0.5 + txt.points.box.height))
for char in txt[0]
],
lag_ratio=0.1,
)
self.forward()
## Part 2: Mask and text transformation
# Transform the mask to a circle
mask2 = ShapeMask(txt, shape=Circle())
self.play(Transform(mask1, mask2))
self.forward()
# Mask feathering
rect = Rect(3, 3, color=LIGHT_BROWN, fill_alpha=1, depth=10)
self.play(FadeIn(rect))
self.play(
mask2.anim.points.shift(UP * 0.5)
.r.feather.set(0.1),
)
self.play(
Succession(
txt.anim.points.shift(LEFT * 1),
txt.anim.points.shift(RIGHT * 1),
)
)
self.forward()
# Transform while holding the item
txt2 = Text("The mask should be hold")
txt2.points.scale(2)
mask2.apply(txt2)
self.play(TransformMatchingDiff(txt, txt2))
self.forward()
# Fade out of the mask itself
self.play(
FadeOut(rect),
FadeOut(mask2),
)
self.forward()
self.play(FadeOut(txt2))
self.forward()
## Part 3: Mask with boolean operations
dot1 = Dot(LEFT, 1.5, color=YELLOW, fill_alpha=0.25).show()
dot2 = Dot(RIGHT, 1.5, color=YELLOW, fill_alpha=0.25).show()
txt3 = Text('Some Example Text Here', font_size=40).show()
# Create mask union and intersection using boolean_ops
mask_union = ShapeMask(
txt3,
shape=boolean_ops.Union(dot1, dot2)
)
mask_intersection = ShapeMask(
txt3,
shape=boolean_ops.Intersection(dot1, dot2)
)
self.forward()
self.play(FadeIn(mask_union))
self.forward(0.5)
self.play(Transform(mask_union, mask_intersection), duration=0.5)
self.forward(0.5)
self.play(Transform(mask_intersection, mask_union), duration=0.5)
self.forward(0.5)
self.play(FadeOut(Group(dot1, dot2, txt3)))
self.forward()
## Part 4: Complex shape mask with special effects
# Complex shape mask
txt_fashion = Text('Fashion')
txt_fashion.points.scale(10)
dots = DotCloud(
*[
[i * 0.3 + 0.15, j * 0.3, 0]
for j in range(20, -40, -1)
for i in range(-23, 23)
],
radius=0.1,
color=PURPLE_E,
)
mask3 = ShapeMask(dots, shape=txt_fashion).show()
self.prepare(
dots.anim(rate_func=linear).points.shift(UP * 3),
duration=5.0,
)
self.play(FadeIn(dots))
self.forward(1.5)
self.play(mask3.anim.invert.set(1.0))
self.forward(1.5)
See also:
JAnim - 编写开源动画引擎的一次尝试by jkjkil-jiang
Well, this itself is made with JAnim
【伪科普/整活向】如何计算 1 + 1by _F_X_P
用重要极限证明欧拉公式by Cirpix
2017年高联B卷第10题by Cirpix
用排序不等式证明柯西不等式by Cirpix
视频是如何存储在你的设备里的 - 视频编码简述by jkjkil-jiang
【漫士】这是世界上增长最快的数列,不服来战by 漫士沉思录 & LifeCheckpoint
Python 图形编程 | 入门篇 第一部分 绘制一个三角形 | 1-基本介绍by jkjkil-jiang
Python 图形编程 | 入门篇 第一部分 绘制一个矩形 | 2-OpenGL具体介绍by jkjkil-jiang
Python 图形编程 | 入门篇 第一部分 绘制一个矩形 | 5-你好,三角形by jkjkil-jiang
Python 图形编程 | 入门篇 第二部分 完善你的三角形 | 6-索引缓冲对象by jkjkil-jiang
Python 图形编程 | 入门篇 第二部分 完善你的三角形 | 7-着色器by jkjkil-jiang
Python 图形编程 | 入门篇 第二部分 完善你的三角形 | 8-更多顶点属性by jkjkil-jiang
Python 图形编程 | 入门篇 第三部分 纹理 | 9-纹理的基本概念by jkjkil-jiang
基于 JAnim 的舞萌DX播放器by jkjkil-jiang
【伪·Rizline自制谱】绝涮双娇by 破壁人五号
数千行代码,带来拜年纪燃曲《天星问》Rizline自制谱!!1(动画)by 破壁人五号
「不想死于像这样活着」 | 【伪·Rizline自制谱】人造卫星by 破壁人五号
【伪·Rizline自制谱】ギターと孤独と蒼い惑星by 破壁人五号
Python 图形编程 | 入门篇 第一部分 绘制一个矩形 | 3-安装与创建窗口by jkjkil-jiang
Segments appearing as video overlay animations, excluding the “Slide-style Demonstration” from 00:00 - 01:25 :
03:18 - 03:31 : Demonstrates the parameter meanings of the
create_windowfunction03:57 - 04:48 : Demonstrates the concept of
viewport
Python 图形编程 | 入门篇 第一部分 绘制一个矩形 | 4-处理输入与背景颜色by jkjkil-jiang
Segments appearing as video overlay animations, excluding the “Slide-style Demonstration” from 00:00 - 00:25 :
00:50 - 01:02 : Demonstrates the usage of
get_key01:10 - 01:17: Emphasis on the “rendering loop” code section
02:01 - 02:18 : Demonstrates the concept of
RGBcolors
Python 图形编程 | 入门篇 第三部分 纹理 | 10-加载与创建纹理by jkjkil-jiang
Segments appearing as video overlay animations (some less important parts are omitted due to too many segments):
00:58 - 01:12 : Demonstrates the members of
img02:43 - 02:52 : Notes that appear when following cursor editing
02:52 - 02:57 : Illustrates color sampling
03:28 - 03:36 : Illustrates sampling parameters
03:54 - 04:14 : Illustrates texture binding
06:04 - 06:22 : Demonstrates the color blending effect of the
mixfunction07:42 - 08:01 : Explains the reason for texture being upside down
Installation and Quick Start
If you encounter difficulties during installation or have any questions while using it, you can ask questions on the Github Discussions Page or join QQ group 970174336
Basic Tutorials - Understanding Basic Concepts
GUI Tutorials - Using the JAnim GUI
Resource Tutorials - Using External Assets
Advanced Tutorials - Mastering More Practical Mechanisms
Other Tutorials
Other
Example Index
Development