Introduction to Taichi Programming
Introduction to Taichi Programming
Programming
Language
Yuanming Hu
Getting started
Meta- Yuanming Hu
programming
Differentiable
Programming
Debugging
Visualization
1 / 52
What is Taichi?
The Taichi
Programming
Language
Yuanming Hu
High-performance domain-specific language (DSL) embedded in Python, for
Getting started
computer graphics applications
Data
Computation
● Productivity and portability: easy to learn, to write, and to share
Objective ● Performance: data-oriented, parallel, megakernels
data-oriented
programming ● Spatially sparse programming: save computation and storage on empty
Meta-
programming regions
Differentiable ● Decouple data structures from computation
Programming
Debugging
● Differentiable programming support
Visualization
2 / 52
Taichi v.s. deep learning frameworks
The Taichi
Programming Why is Taichi different from TensorFlow, PyTorch, NumPy, JAX, ... ?
Language
Yuanming Hu Quick answer: Taichi uniquely supports megakernels and spatial sparsity.
Getting started
Data
Longer answer: Those systems serve their own application domains (e.g.,
Computation
convolutional neural networks) very well, but their design decisions surrounding
Objective immutable, dense tensors (e.g., feature maps) with simple, regular operators
data-oriented
programming
(e.g., element-wise add and 2D convolutions) do not serve well more irregular
Meta- computational patterns, including
programming
● Computer graphics, including physical simulation and rendering
Differentiable
Programming
● Irregular neural network layers (e.g., gathering/scattering) that are emerging
Debugging
● General differentiable programming cases
Visualization
Without Taichi people tend to manually write CUDA or abuse deep learning
programming interfaces. Taichi offers performance, productivity, and portability in
those cases.
3 / 52
Hello, world! (Julia set, z ← z2 + c)
The Taichi
Programming import taichi as ti
Language [Link](arch=[Link])
n = 320
Yuanming Hu pixels = [Link](dtype=float , shape =(n * 2, n))
@[Link]
Getting started def complex_sqr(z):
return [Link] ([z[0]**2 - z[1]**2 , z[1] * z[0] * 2])
Data
Computation @[Link]
def paint(t: float):
Objective for i, j in pixels: # Parallized over all pixels
data-oriented c = [Link] ([-0.8, [Link](t) * 0.2])
programming z = [Link] ([i / n - 1, j / n - 0.5]) * 2
iterations = 0
Meta- while [Link] () < 20 and iterations < 50:
programming z = complex_sqr(z) + c
iterations += 1
Differentiable pixels[i, j] = 1 - iterations * 0.02
Programming
gui = [Link]("Julia Set", res=(n * 2, n))
Debugging
for i in range (1000000):
Visualization
paint(i * 0.03)
gui.set_image(pixels)
[Link] ()
Yuanming Hu
Getting started
Data
Computation
Objective
data-oriented
programming
Meta-
programming
Differentiable
Programming
Debugging
Visualization
5 / 52
Overview
The Taichi
Programming
Language
This talk serves as an introductory course on the syntax of the Taichi
Yuanming Hu programming language.
● Advanced topics such as data layout specification, sparse data structures, and
Getting started
Data
advanced differentiable programming will not be covered in this 1-hour
Computation course.
Objective ● Slides will be actively updated after the course to keep up with the latest
data-oriented
programming Taichi system (v0.6.22).
Meta-
programming
● More details are available in the Taichi documentation (English & Simplified
Differentiable Chinese).
Programming
Debugging
Note
Visualization
Many features of Taichi are developed by the Taichi community.
Clearly, I am not the only developer :-)
6 / 52
Table of Contents
The Taichi
Programming
Language 1 Getting started
Yuanming Hu
2 Data
Getting started
Data
3 Computation
Computation
Objective
data-oriented
4 Objective data-oriented programming
programming
Meta- 5 Meta-programming
programming
Differentiable
Programming
6 Differentiable Programming
Debugging
7 Debugging
Visualization
8 Visualization
7 / 52
Installation
The Taichi
Programming
Language
Yuanming Hu
Taichi can be installed via pip on 64-bit Python 3.6/3.7/3.8:
Getting started
Objective
data-oriented
Notes
programming
Meta-
● Taichi supports Windows, Linux, and OS X.
programming
● Taichi runs on both CPUs and GPUs (CUDA/OpenGL/Apple Metal).
Differentiable
Programming ● Build from scratch if your CPU is AArch64 or you use Python 3.9+.
Debugging
Visualization
8 / 52
Digression: Taichi’s command line interface
The Taichi
Programming
Language
Yuanming Hu
Use python3 -m taichi or simply ti to start Taichi’s CLI.
Getting started
Objective
● ti example: list all examples
data-oriented
programming ● ti example mpm99/sdf_renderer/autodiff_regression/...: run an example
Meta-
programming
● ti example -p/-P [example]: show the code of the example
Differentiable Taichi has 40+ minimal language examples. Playing with them is the easiest way
Programming
Debugging
to learn about this language (and to have fun).
Visualization
9 / 52
Initialization
The Taichi
Programming
Language
Yuanming Hu Always initialize Taichi with [Link]() before you do any Taichi operations.
Getting started For example,
Data [Link](arch=[Link])
Computation
Objective The most useful argument: arch, i.e., the backend (architecture) to use
data-oriented
programming ● ti.x64/arm/cuda/opengl/metal: stick to a certain backend.
Meta-
programming ● [Link] (default), automatically detects x64/arm CPUs.
Differentiable
Programming
● [Link], try cuda/metal/opengl. If none is detected, Taichi falls back on CPUs.
Debugging
10 / 52
Table of Contents
The Taichi
Programming
Language 1 Getting started
Yuanming Hu
2 Data
Getting started
Data
3 Computation
Computation
Objective
data-oriented
4 Objective data-oriented programming
programming
Meta- 5 Meta-programming
programming
Differentiable
Programming
6 Differentiable Programming
Debugging
7 Debugging
Visualization
8 Visualization
11 / 52
Data types
The Taichi
Programming
Language
Yuanming Hu
Taichi is statically and strongly and typed. Supported types include
● Signed integers: ti.i8/i16/i32/i64
Getting started
Data
● Unsigned integers: ti.u8/u16/u32/u64
Computation ● Float-point numbers: ti.f32/f64
Objective
data-oriented ti.i32 and ti.f32 are the most commonly used types in Taichi. Boolean values
programming
are represented by ti.i32 for now.
Meta-
programming
Data type compatibility
Differentiable
Programming
The CPU and CUDA backends support all data types. Other backend may miss
Debugging
certain data type support due to backend API constraints. See the documentation
Visualization
for more details.
12 / 52
Fields
The Taichi
Programming
Language
Yuanming Hu
Taichi is a data-oriented programming language where fields are first-class
Getting started
citizens.
Data
Computation
● Fields are essentially multi-dimensional arrays
Objective ● An element of a field can be either a scalar ([Link]), a vector
data-oriented
programming ([Link]), or a matrix ([Link])
Meta-
programming
● Field elements are always accessed via the a[i, j, k] syntax. (No pointers.)
Differentiable ● Access out-of-bound is undefined behavior in non-debug mode
Programming
Debugging
● (Advanced) Fields can be spatially sparse
Visualization
13 / 52
Table of Contents
The Taichi
Programming
Language 1 Getting started
Yuanming Hu
2 Data
Getting started
Data
3 Computation
Computation
Objective
data-oriented
4 Objective data-oriented programming
programming
Meta- 5 Meta-programming
programming
Differentiable
Programming
6 Differentiable Programming
Debugging
7 Debugging
Visualization
8 Visualization
14 / 52
Kernels
The Taichi
Programming
In Taichi, computation resides in kernels.
Language
1 The language used in Taichi kernels is similar to Python
Yuanming Hu
2 The Taichi kernel language is compiled, statically-typed, lexically-scoped,
Getting started
parallel and differentiable
Data
3 Taichi kernels must be decorated with @[Link]
Computation
Meta-
Examples
programming
15 / 52
Functions
The Taichi
Programming
Taichi functions (@[Link]) can be called by Taichi kernels and other Taichi
Language functions. No type-hints needed for arguments and return values in @[Link].
Yuanming Hu
Examples
Getting started
Data @[Link]
Computation def triple (x):
Objective return x * 3
data-oriented
programming
@ti. kernel
Meta-
programming def triple_array ():
Differentiable
for i in range (128):
Programming a[i] = triple (a[i])
Debugging
Visualization
Note
Taichi functions will be force-inlined. For now, recursion is not allowed.
A Taichi function can contain at most one return statement.
16 / 52
Scalar math
The Taichi
Programming
Language
Data
Math functions:
Computation
17 / 52
Matrices and linear algebra
The Taichi
Programming [Link] is for small matrices (e.g. 3 × 3) only. If you have 64 × 64 matrices,
Language
Yuanming Hu
please consider using a 2D scalar field.
[Link] is the same as [Link], except that it has only one column.
Getting started
Common matrix operations:
Data
Visualization
Warning
Element-wise product * and matrix product @ have different behaviors.
18 / 52
Parallel for-loops
The Taichi
Programming
Language
Yuanming Hu
Two types of for loops in Taichi:
Getting started
● Range-for loops, which are no different from Python for loops, except that
Data
Computation
it will be parallelized when used at the outermost scope. Range-for loops can
Objective
be nested.
data-oriented
programming ● Struct-for loops, which iterates over (sparse) field elements. (More on this
Meta- later.)
programming
Differentiable
Programming
For loops at the outermost scope in a Taichi kernel are automatically
Debugging
parallelized.
Visualization
19 / 52
Range-for loops
The Taichi
Programming Examples
Language
Yuanming Hu
@ti. kernel
Getting started
def fill ():
for i in range (10): # Parallelized
Data
x[i] += i
Computation
Objective s = 0
data-oriented
programming for j in range (5): # Serialized in each parallel thread
Meta-
s += j
programming
Differentiable y[i] = s
Programming
21 / 52
Struct-for loops
The Taichi
Programming Examples
Language
Yuanming Hu
import taichi as ti
Getting started
[Link](arch=[Link])
Data
Computation
n = 320
Objective pixels = [Link](dtype =ti.f32 , shape =(n * 2, n))
data-oriented
programming
Meta-
@ti. kernel
programming def paint (t: ti.f32):
Differentiable for i, j in pixels : # Parallized over all pixels
Programming
pixels [i, j] = i * 0.001 + j * 0.002 + t
Debugging
The struct-for loops iterates over all the field coordinates, i.e.
(0, 0), (0, 1), (0, 2), ..., (0, 319), (1, 0), ..., (639, 319).
22 / 52
Atomic operations
The Taichi
Programming
In Taichi, augmented assignments (e.g., x[i] += 1) are automatically atomic.
Language
Yuanming Hu Examples
Getting started When modifying global variables in parallel, make sure you use atomic operations.
Data For example, to sum up all the elements in x,
Computation
@ti. kernel
Objective def sum ():
data-oriented
programming for i in x:
Meta- # Approach 1: Correct
programming total [None] += x[i]
Differentiable
Programming
# Approach 2: Correct
Debugging ti. atomic_add ( total[None], x[i])
Visualization
23 / 52
Taichi-scope v.s. Python-scope
The Taichi
Programming
Language
Definition
Yuanming Hu
Taichi-scope: Everything decorated with [Link] and [Link].
Getting started
Data
Computation
Definition
Objective Python-scope: Code outside Taichi-scope.
data-oriented
programming
Meta- Note
programming
Differentiable 1 Code in Taichi-scope will be compiled by the Taichi compiler and run on
Programming
Debugging
parallel devices.
Visualization 2 Code in Python-scope is simply Python code and will be executed by the
Python interpreter.
24 / 52
Playing with fields in Taichi-scope
The Taichi
Programming
Language Of course, fields can be manipulated in Taichi-scope as well:
Yuanming Hu import taichi as ti
[Link] ()
Getting started
a = [Link](dtype=ti.f32 , shape =(42, 63)) # A field of 42x63 scalars
Data b = [Link](3, dtype=ti.f32 , shape =4) # A field of 4x 3D vectors
C = [Link](2, 2, dtype=ti.f32 , shape =(3, 5)) # A field of 3x5 2x2 matrices
Computation
@[Link]
Objective def foo():
data-oriented a[3, 4] = 1
programming print('a[3, 4] =', a[3, 4])
# "a[3, 4] = 1.000000"
Meta-
programming
b[2] = [6, 7, 8]
Differentiable print('b[0] =', b[0], ', b[2] =', b[2])
Programming # "b[0] = [[0.000000] , [0.000000] , [0.000000]] , b[2] = [[6.000000] , [7.000000] , [8.000000]]"
foo()
25 / 52
Phases of a Taichi program
The Taichi
Programming
Language
Yuanming Hu
1 Initialization: [Link](...)
Getting started
2 Field allocation: [Link], [Link], [Link]
Data
26 / 52
Putting everything together: [Link]
The Taichi
Programming import taichi as ti
Language
[Link](arch=[Link])
Yuanming Hu
n = 320
pixels = [Link](dtype=ti.f32 , shape =(n * 2, n))
Getting started
@[Link]
Data def complex_sqr(z):
return [Link] ([z[0]**2 - z[1]**2 , z[1] * z[0] * 2])
Computation
Objective @[Link]
data-oriented def paint(t: ti.f32):
programming for i, j in pixels: # Parallized over all pixels
c = [Link] ([-0.8, [Link](t) * 0.2])
Meta- z = [Link] ([i / n - 1, j / n - 0.5]) * 2
programming iterations = 0
while [Link] () < 20 and iterations < 50:
Differentiable z = complex_sqr(z) + c
Programming iterations += 1
pixels[i, j] = 1 - iterations * 0.02
Debugging
gui = [Link]("Julia Set", res=(n * 2, n))
Visualization
for i in range (1000000):
paint(i * 0.03)
gui.set_image(pixels)
[Link] ()
27 / 52
Table of Contents
The Taichi
Programming
Language 1 Getting started
Yuanming Hu
2 Data
Getting started
Data
3 Computation
Computation
Objective
data-oriented
4 Objective data-oriented programming
programming
Meta- 5 Meta-programming
programming
Differentiable
Programming
6 Differentiable Programming
Debugging
7 Debugging
Visualization
8 Visualization
28 / 52
ODOP: Using classes in Taichi
The Taichi
Programming
Language
29 / 52
ODOP: An example
The Taichi
Programming Demo: ti example odop_solar a = GMr/∣∣r∣∣32
Language
import taichi as ti
Yuanming Hu
@ti.data_oriented
Getting started
class SolarSystem:
Data def __init__(self , n, dt):
self.n = n
Computation [Link] = dt
self.x = [Link](2, dtype=ti.f32 , shape=n)
Objective self.v = [Link](2, dtype=ti.f32 , shape=n)
data-oriented [Link] = [Link](2, dtype=ti.f32 , shape =())
programming
@staticmethod
Meta- @[Link]
programming def random_around(center , radius):
# random number in [center - radius , center + radius)
Differentiable
return center + radius * ([Link] () - 0.5) * 2
Programming
Debugging @[Link]
def initialize(self):
Visualization for i in range(self.n):
offset = [Link] ([0.0 , self.random_around (0.3, 0.15) ])
self.x[i] = [Link][None] + offset
self.v[i] = [-offset [1], offset [0]]
self.v[i] *= 1.5 / [Link] ()
30 / 52
ODOP: An example (continued)
The Taichi
Programming @[Link]
Language def gravity(self , pos):
offset = -(pos - [Link][None ])
Yuanming Hu return offset / [Link] ()**3
@[Link]
Getting started
def integrate(self):
Data for i in range(self.n):
self.v[i] += [Link] * [Link](self.x[i])
Computation self.x[i] += [Link] * self.v[i]
Objective
data-oriented solar = SolarSystem (9, 0.0005)
programming [Link][None] = [0.5, 0.5]
[Link] ()
Meta-
programming gui = [Link]("Solar System", background_color =0 x25A6D9)
Differentiable
while True:
Programming
if gui.get_event ():
Debugging if [Link] == [Link] and [Link] == [Link]:
[Link] ()
Visualization for i in range (10):
[Link] ()
[Link] ([0.5 , 0.5], radius =20, color =0 x8C274C)
[Link](solar.x.to_numpy (), radius=5, color =0 xFFFFFF)
[Link] ()
31 / 52
Table of Contents
The Taichi
Programming
Language 1 Getting started
Yuanming Hu
2 Data
Getting started
Data
3 Computation
Computation
Objective
data-oriented
4 Objective data-oriented programming
programming
Meta- 5 Meta-programming
programming
Differentiable
Programming
6 Differentiable Programming
Debugging
7 Debugging
Visualization
8 Visualization
32 / 52
Metaprogramming
The Taichi
Programming
Language
Yuanming Hu
Taichi provides metaprogramming tools. Metaprogramming can
Getting started
● Allow users to pass almost anything (including Taichi fields) to Taichi kernels
Data
● Improve run-time performance by moving run-time costs to compile time
Computation
Visualization
template arguments.
33 / 52
Templates
The Taichi
Programming
Language
@ti. kernel
Yuanming Hu def copy(x: ti. template (), y: ti. template (), c: ti.f32):
for i in x:
Getting started
y[i] = x[i] + c
Data
Computation
Debugging
Template argument takes (almost) everything
Visualization Feel free to pass fields, classes, functions, strings, and numerical values to
arguments hinted as [Link]().
34 / 52
Template kernel instantiation
The Taichi
Programming Be careful!
Language
Yuanming Hu
import taichi as ti
[Link] ()
Getting started
Differentiable
Programming
@ti. kernel
def world (i: ti.i32):
Debugging
print(i)
Visualization
35 / 52
Dimensionality-independent programming
The Taichi
Programming
Language
Examples
Yuanming Hu
@ti. kernel
Getting started def copy(x: ti. template (), y: ti. template ()):
Data for I in ti. grouped (y):
Computation
x[I] = y[I]
Objective
data-oriented @ti. kernel
programming def array_op (x: ti. template (), y: ti. template ()):
Meta- for I in ti. grouped (x):
programming
# I is a vector of size [Link] () and dtype i32
Differentiable
Programming
y[I] = I[0] + I[1]
# If x is 2D field , the above is equivalent to
Debugging
for i, j in x:
Visualization
y[i, j] = i + j
Application: write simulation code that works for both 2D & 3D.
36 / 52
Field-size reflection
The Taichi
Programming
Language
Yuanming Hu
Fetch field dimensionality info as compile-time constants:
import taichi as ti
Getting started
Data
[Link] ()
Computation
field = ti. field (dtype =ti.f32 , shape =(4, 8, 16, 32, 64))
Objective
data-oriented
programming @ti. kernel
Meta-
def print_shape (x: ti. template ()):
programming ti. static_print (x. shape)
Differentiable for i in ti. static ( range(len([Link]))):
Programming
print ([Link] [i])
Debugging
37 / 52
Compile-time branching
The Taichi
Programming
Language
Yuanming Hu
Using compile-time evaluation will allow certain computations to happen when
Getting started
kernels are being instantiated. This saves the overhead of those computations at
Data
Computation
runtime. (C++17 equivalence: if constexpr.)
Objective enable_projection = True
data-oriented
programming
@ti. kernel
Meta-
programming def static ():
Differentiable if ti. static ( enable_projection ): # No runtime overhead
Programming x[0] = 1
Debugging
Visualization
38 / 52
Forced loop-unrolling
The Taichi
Programming
Language
Use [Link](range(...)) to unroll the loops at compile time:
Yuanming Hu
import taichi as ti
Getting started
Data [Link] ()
Computation x = ti. Vector .field (3, dtype=ti.i32 , shape =16)
Objective
data-oriented @ti. kernel
programming
def fill ():
Meta-
programming
for i in x:
for j in ti. static ( range (3)):
Differentiable
Programming x[i][j] = j
Debugging print (x[i])
Visualization
fill ()
39 / 52
Forced loop-unrolling
The Taichi
Programming
Language
Yuanming Hu
Computation
● To optimize for performance.
Objective ● To loop over vector/matrix elements. Indices into Taichi vectors or matrices
data-oriented
programming must be compile-time constants. Indices into Taichi fields can be run-time
Meta- variables. For example, if x is a 1D field of 3D vectors, accessed as
programming
x[field_index][matrix_index]. The first index can be a variable, yet the
Differentiable
Programming second must be a compile-time constant.
Debugging
Visualization
40 / 52
Variable aliasing
The Taichi
Programming
Language
Taichi allows programmers to create aliases using [Link]. For example,
Yuanming Hu
a = [Link](a_field_or_kernel_with_very_long_name).
This can sometimes improve readability. For example,
Getting started
Data
@ti. kernel
def my_kernel ():
Computation
for i, j in field_a :
Objective
data-oriented
field_b [i, j] = some_function ( field_a [i, j]) + some_function
programming ( field_a [i + 1, j])
Meta-
programming
can be simplified into
Differentiable
Programming @ti. kernel
Debugging def my_kernel ():
Visualization a, b, fun = ti. static (field_a , field_b , some_function )
for i,j in a:
b[i,j] = fun(a[i,j]) + fun(a[i + 1,j])
41 / 52
Table of Contents
The Taichi
Programming
Language 1 Getting started
Yuanming Hu
2 Data
Getting started
Data
3 Computation
Computation
Objective
data-oriented
4 Objective data-oriented programming
programming
Meta- 5 Meta-programming
programming
Differentiable
Programming
6 Differentiable Programming
Debugging
7 Debugging
Visualization
8 Visualization
42 / 52
Differentiable Programming
The Taichi
Programming
Language
Yuanming Hu
∂ f(x)
Getting started
Forward programs evaluate f(x); backward (gradient) programs evaluate ∂x .
Data
Differentiable
1 Use Taichi’s tape ([Link](loss)) for both forward and gradient evaluation.
Programming
2 Explicitly use gradient kernels for gradient evaluation with more controls.
Debugging
Visualization
43 / 52
Gradient-based optimization
The Taichi
Programming
Language 1 n−1 2
minx L(x) = ∑ (xi − yi ) .
Yuanming Hu 2 i=0
Getting started
1 Allocating fields with gradients:
Data
x = [Link](dtype=ti.f32, shape=n, needs_grad=True)
Computation
Debugging
3Compute loss with [Link](loss=L): reduce()
Visualization
4 Gradient descent: for i in x: x[i] -= [Link][i] * 0.1
Debugging
3 In a [Link](loss=potential), call the forward kernels.
Visualization 4 Force on each particle is -[Link][i].
45 / 52
Application 2: Differentiating a whole physical process
The Taichi
Programming
Language
10 Demos: DiffTaichi (xt+1 , vt+1 , ...) = F(xt , vt , ...)
Yuanming Hu Pattern:
Getting started
with [Link](loss=loss):
for i in range(steps - 1):
Data
simulate (i)
Computation
Objective
data-oriented
programming Computational history
Meta-
programming Always keep the whole computational history of time steps for end-to-end
Differentiable differentiation. I.e., instead of only allocating
Programming
[Link](3, dtype=ti.f32, shape=(num_particles)) that stores the latest
Debugging
particles, allocate for the whole simulation process
Visualization
[Link](3, dtype=ti.f32, shape=(num_timesteps, num_particles)). Do not
overwrite! (Use checkpointing to reduce memory consumption.)
46 / 52
Table of Contents
The Taichi
Programming
Language 1 Getting started
Yuanming Hu
2 Data
Getting started
Data
3 Computation
Computation
Objective
data-oriented
4 Objective data-oriented programming
programming
Meta- 5 Meta-programming
programming
Differentiable
Programming
6 Differentiable Programming
Debugging
7 Debugging
Visualization
8 Visualization
47 / 52
Debug mode
The Taichi
Programming
initializes Taichi in debug mode, which enables
[Link](debug=True, arch=[Link])
Language bound checkers (CPU and CUDA). See the doc more on debug mode.
Yuanming Hu
Examples
Getting started
Differentiable
@ti. kernel
Programming def shift ():
Debugging for i in range (10):
Visualization
a[i] = b[i + 1] # Runtime error (out -of -bound)
assert i < 5 # Runtime assertion failure
shift ()
48 / 52
Table of Contents
The Taichi
Programming
Language 1 Getting started
Yuanming Hu
2 Data
Getting started
Data
3 Computation
Computation
Objective
data-oriented
4 Objective data-oriented programming
programming
Meta- 5 Meta-programming
programming
Differentiable
Programming
6 Differentiable Programming
Debugging
7 Debugging
Visualization
8 Visualization
49 / 52
Visualize you results
The Taichi
Programming
Language
Differentiable
Visualizing 3D results
Programming
Debugging
Exporting 3D particles and meshes using [Link] [doc]
Visualization
Demo: ti example export_ply/export_mesh
Use Houdini/Blender to view (and render) your 3D results.
50 / 52
Making a video
The Taichi
Programming
Language Make an mp4 video out of your 2D frames
Yuanming Hu
1 Use [Link] [doc] to save the screenshots. Or simply use
Getting started
[Link](img, filename) [doc].
Data
Computation
2 ti video creates video.mp4 using frames under the current folder. To specify
Objective frame rate, use ti video -f 24 or ti video -f 60.
data-oriented
programming 3 Convert mp4 to gif and share it online: ti gif -i input.mp4.
Meta-
programming
Debugging ● Linux and OS X: with high probability you already have ffmpeg.
Visualization
● Windows: install ffmpeg on your own [doc].