"Totally not manim in nim" - Erik
Tested and works on:
Linux | Windows | MacOS
Nanim is an easy-to-use framework to create smooth GPU-accelerated animations that can be previewed live inside a glfw window and, when ready, rendered to videos at an arbitrary resolution and framerate.
I have a series of animations made using nanim posted to my Instagram Page. Some of them include:
I also post art to my OpenSea Page where they can be bought as NFT:s.
Simply do nimble install nanim
do git clone git@github.com:EriKWDev/nanim.git --depth=1
, cd nanim
and finally nimble install
. Once installed, you can add the optional dependency ffmpeg. For debian/ubuntu that would be sudo apt install ffmpeg
. This allows you to render your animations to videos using --render
.
Run one of the examples using nim c -r examples/example_001.nim --size:700 --debug:false
!
Create a normal nim program where you create a Nanim Scene. This scene will carry the state of all animations and entities. This is what the bare minimum looks like:
import nanim
proc testScene(): Scene =
let scene = newScene()
return scene
when isMainModule:
render(testScene)
But that's not very fun! Let's add some animation! Here is what a simple scene might look like:
# example.nim
import nanim
proc testScene(): Scene =
# Creates a scene-state
let scene = newScene()
# You can load some nice colors from a palette on coolors.co!
var colors = colorsFromCoolors("https://coolors.co/33658a-86bbd8-758e4f-f6ae2d-f26419")
scene.randomize() # randomize the seed of the scene
colors.shuffle()
let bg = colors[0]
colors.del(0)
scene.setBackgroundColor(bg)
# We can have text too! You have to put your TTF-fonts in a 'fonts' directory
# next to the binary. 'Montserrat-Thin.ttf' will be known as 'montserrat-thin'
var
text = newText("Hello, World!", font="montserrat-thin")
rect = newSquare()
# We must add our entities to the scene in order for them to be drawn
scene.add(rect, text)
# Set some colors!
text.fill(colors[1])
rect.fill(colors[2])
rect.stroke(colors[3], 4.0)
# By discarding tweens, we can "set" values without animating the change
discard text.move(150, 150)
scene.wait(500)
scene.showAllEntities()
scene.wait(500)
# scene.play() and scene.animate() animates any number of tweens and
# can be used interchangeably
scene.play(text.move(500, 500),
rect.move(100, 500),
rect.rotate(45))
scene.animate(rect.pscale(3))
scene.play(rect.setTension(0.6))
scene.wait(500)
scene.play(rect.pscale(1/3))
scene.play(rect.setTension(0),
rect.rotate(360*2),
rect.pscale(4))
scene.wait(500)
scene.play(rect.move(600), rect.setCornerRadius(30))
# Want to repeat an animation? Simply add a loop!
for i in 0..5:
scene.play(rect.move(-20),
rect.rotate(-300),
rect.pscale(if i mod 2 == 0: 1.0/10.0 else: 10.0))
scene.wait(500)
# ..and finally return our scene. Scenes don't have to be created inside a proc/func like
# this one, but it helps a lot when we want to combine multiple scenes in the future, so
# it should be considered "best practice".
return scene
when isMainModule:
# Finally, call render to render our scene.
# render(testScene()) works as well.
render(testScene)
The scenes can then be run by simply compiling the file like so: nim c -r <file_containing_scene>.nim
. Once your scene is compiled, you can run it either in "live" mode (default), which opens a window and renders the scene in realtime where you can scrub around with your mouse and resize the window at will. That, or you can render it to a video by supplying --render
with a specified --size:<size>
after. (Make Sure ffmpeg
is installed for this to work!).
Here are all the options (keep in mind that it is the last option(s) supplied that takes priority over others):
Options:
-v, --video, --render
Enables video rendering mode. Will output video to renders/<name>.mp4
--gif
WARNING: Huge files. Please use with --size:400 --fps:15 or, preferably,
manually convert the mp4 from --render to a GIF.
Enables gif rendering mode. Will output gif to renders/<name>.gif
--snap, --screenshot, --image, --picture, --png
Will create a PNG screenshot of the Scene. Will output to renders/<name>.png
--fullhd, --1080p
width 1920, height 1080 (16:9)
--2k, --1440p
width 2880, height 2560 (18:9)
--4k, --2160p
width 3840, height 2160 (18:9)
--shorts
width 1440, height 2560 (9:16)
--square
width 1000, height 1000 (1:1)
--ratio:W:H
Sets the ratio between width and height. Example: --ratio:16:9 --width:1920
will set width to 1920 and height to 1080
-w:WIDTH, --width:WIDTH
Sets width to WIDTH
-h:HEIGHT, --height:HEIGHT
Sets height to HEIGHT
-s:SIZE, --size:SIZE
Sets both width andd height to SIZE
--fps:FPS, --rate:FPS
Sets the desired framerate to FPS
--debug:true|false
Enables debug mode which will visualize the scene's tracks.
Default behaviour is to show the visualization in live mode
but not in render mode.
Remember that the rendering to video requires FFMpeg to be installed and available in your PATH
.
- The majority[1] of the source for this project is released under the MIT license. See the
LICENSE
file for details on what this means for distribution. All source currently in this repository is free. - The Montserrat font families used in the examples in the
examples/fonts
directory are used under the OFL and are subject to copyright by The Montserrat Project Authors (https://github.com/JulietaUla/Montserrat). Seeexamples/fonts/OFL.txt
- This project has no association with 3b1b nor ManimCommunity's
manim
, but is indeed inspired by it.
[1]: Some files, like artwork and special entities, are not made public currently. This might change in the future.