Shots in this scene
Lottie: Animation as JSON
In one line
A Lottie file is a recipe: layers, shapes and keyframes written as JSON. A player redraws it every frame, so it stays sharp at any size. A .lottie file is the same recipe zipped. A player ignores what it cannot draw, without an error.
Think of it like this
Sheet music. The paper does not contain sound; it holds notes, timing and instructions, and a musician (the player) performs it. Two musicians can play it at different sizes of hall. A Lottie file holds shapes, positions and timing, and a player draws them. A recording (video) would fix one performance.
Words you'll meet
New words in this topic, in plain English. Come back here whenever one feels fuzzy.
- layer
- One drawn item in the animation with its own transform and shapes. The first layer in the list is drawn on top.
- keyframe
- A value stored for one frame. The player works out the frames between two keyframes.
- marker
- A named stretch of the timeline, stored as a start frame and a length.
- dotLottie
- The
.lottiefile: a ZIP with a manifest, the animation JSON and optional images, themes and state machines. - player
- The library that reads the file and draws each frame onto a canvas.
- expression
- A small script inside a Lottie property (such as
wiggle). Players that do not support them skip it.
Step by step
01Open the JSON as data
The first example reads headlight.json and prints its header, its layers, how many keyframes each one has, and its markers. You are looking at the whole animation as text: 6,592 characters.
02Find the three stretches
The markers give greet 0+45, blink 45+15 and idle 60+60: start frame plus length. At 30 fps they are 1.5 s, 0.5 s and 2 s, and together they cover the 4 seconds (120 frames).
03Edit the file and load it
The second example changes the JSON in memory and gives it to the player with the data option. It loads five variants and compares the pixels at frame 30 with the original.
04Read the verdicts
Three variants load and look identical (their feature is ignored). One breaks the file's structure and fails with Invalid Lottie JSON object. A player is not a validator: it draws what it understands.
Try it yourself
- 1
Change a colour in the data
Add
"face colour red": edit((j) => { j.layers[1].shapes[1].it[1].c.k = [1, 0, 0, 1]; }),to the cases. It printsload, different picture at frame 30, and on the canvas the lamp's orange body is red (the cream face and the dark outline stay as they were): colours in Lottie are red, green, blue, alpha from 0 to 1. - 2
Set the frame rate to 0
Add
"frame rate 0": edit((j) => { j.fr = 0; }),. It loads without an error and gives the same picture at frame 30. The file'sfris 0 but the player did not reject it. If you also printp.durationafterload(the player'sdurationis in seconds), it saysInfinitywhilep.totalFramesis still 120: frames divided by a rate of 0. Check a file's numbers before you trust a duration.
Code & diagrams
The animated values are counted as `{ a: 1, k: [...] }` arrays anywhere inside a layer, so `eyes` (two eyes, each scaled and moved) has the most. `bracket` has none: it never moves.
Nothing is moving yet. Hover, click or scroll the page: every animation it starts shows up here as a track.
Console
Nothing logged yet.Expected console output
version 5.7.4, 30 fps, frames 0 to 120, 200 x 200
layers, top first: eyes, lamp, beam, bracket
eyes: type 4, 26 keyframes
lamp: type 4, 10 keyframes
beam: type 4, 9 keyframes
bracket: type 4, 0 keyframes
markers: greet 0+45, blink 45+15, idle 60+60
assets: 0, size as text: 6592 charactersEach variant loads from edited JSON through the `data` option. The `[error]` line is the library's own console message; the next line is the `loadError` event. The picture comparison uses a hash of the canvas at frame 30.
Nothing is moving yet. Hover, click or scroll the page: every animation it starts shows up here as a track.
Console
Nothing logged yet.Expected console output
as shipped: load, same picture at frame 30
unknown layer type (ty 99): load, same picture at frame 30
blur effect on a layer (ef): load, same picture at frame 30
expression on a property: load, same picture at frame 30
Invalid Lottie JSON object: The provided object does not conform to the Lottie JSON format.
no layers key: loadError Error: Invalid Lottie JSON object: The provided object does not conform to the Lottie JSON format.Break it on purpose
Errors are the best teachers. Make each change, read the error, guess what went wrong, then reveal the answer.
Break #1
Hand the player cut-off text
In the second example, replace data: make() with data: JSON.stringify(make()).slice(0, 3000).
Myth vs fact
Myth
If it plays in After Effects or the design tool's preview, it plays on the web.
Fact
A player draws only what it supports. In our test unsupported features loaded with no error and no change in the picture, so the failure is silent.
Myth
A Lottie file is always small.
Fact
Vector data is small. Embedded pictures are base64 text, a third bigger than the picture itself.
Pro corner
Extra depth for experienced readers. New to this? Skip it for now and come back later.
- ▸
The easing handles
iandouse the same cubic Bézier idea as CSS. A Lottie keyframe can also be a hold (h: 1), which jumps instead of tweening. - ▸
ipandopset the start and end of the whole animation and each layer has its own, so a layer can exist for only part of the timeline. - ▸
The dotLottie manifest can hold several animations, themes (colour sets) and state machines. Our
make-mascot.cjswrites a minimal manifest with one animation and no theme:setTheme()returnsfalseandstateMachineLoad()returnsfalseon it, as measured.
Remember this
- 1
The problem this solves: a designer works in a vector tool such as After Effects and needs the result on a web page without a video. Lottie is the exported recipe, so the page can draw it sharply at any size, play parts of it, and change its speed from code.
- 2
What a file holds. The top level has
v(the format version,5.7.4for ours),fr(frames per second),ipandop(the first and last frame),wandh(the drawing's size),assets(images or nested animations),layersandmarkers. Time is counted in frames: frame 45 at 30 fps is 1.5 seconds. - 3
Layers and keyframes. A shape layer has
ty: 4, anm(name), a transformks(opacityo, rotationr, positionp, anchora, scales) andshapes(ellipses, rectangles, paths, fills). An animated value looks like{ a: 1, k: [{ t: 12, s: [92, 108], i, o }, …] }: at frametthe value iss, andiandoare the Bézier handles of the easing, the same idea as CSScubic-bezier()(Topic 5.2). Layers listed first are drawn on top. - 4
Markers are named stretches of the timeline:
{ tm: 45, cm: "blink", dr: 15 }means 'blink' starts at frame 45 and lasts 15 frames. Mei sets them in the design tool and the page plays them by name (Topic 16.3). Our mascot hasgreet0–45,blink45–60,idle60–120. - 5
**
.jsonand.lottie.** A.lottiefile (dotLottie) is a ZIP: amanifest.jsonplus the animations (and images, themes and state machines when present). Ours is 1,150 bytes against 6,592 for the plain JSON, because text compresses well and the file repeats itself. The olderlottie-weblibrary plays plain JSON with JavaScript drawing into SVG or canvas; the dotLottie player used here draws with a WebAssembly core into a canvas, and downloads a 1.2 MB WASM file once (Topic 16.1). We did not measurelottie-web. - 6
What a player cannot draw, it skips silently. In our test below, a layer of an unknown type, a blur effect and an expression (
wiggle(2, 20)) all loaded without an error and gave exactly the same picture as the original: the feature was ignored. Only a file that is structurally not Lottie fails, and then with a realloadError. So test the exported file in the player you ship, not only in the design tool's preview. - 7
Pictures inside a Lottie are listed under
assetsand are usually base64 text inside the JSON, which is a third larger than the binary picture (4 characters for every 3 bytes). One embedded photo can outweigh all the vector data. Ask the designer to keep a Lottie to vectors and move pictures out.
Explain it without notes
What is the difference between a .json Lottie file and a .lottie file?
Why can a Lottie animation look different on the web from the preview in the design tool?
Why can a Lottie file with one embedded photo be larger than the same animation as a video?
Practice
Write a function markerSeconds(json) that returns each marker's name with its start and end in seconds.
Change the colour of the mascot's orange body (the value the red tryIt edited) to blue (#2a9df4) in the data and load it. What are the red, green and blue numbers?
Done when you can
You can read a Lottie file's frame rate, size, layers and markers from its JSON
You can turn a frame number into seconds
You can say what
.lottieadds to.jsonYou know a player ignores unsupported features silently, and fails loudly only on a broken file