How Lottie stores colour
Colour is scattered across a Lottie document in eight different shapes, and the format specification is thin about most of them. This is what a tool has to handle to recolour a real file correctly.
| Where | Shape | Why it matters |
|---|---|---|
| Solid fill or stroke | shape item `fl` / `st` → `c.k` | Three numbers, normally 0..1. Some exporters write 0..255 instead, so both have to be accepted. |
| Animated fill or stroke | `c.k[i].s` and `c.k[i].e` | One colour per keyframe end. They interpolate into a single painted colour, so they are one editable value even though each keyframe is separately addressable. |
| Gradient | `gf` / `gs` → `g.k.k` | One flat array. The first `g.p` groups of four are `position, r, g, b`. Nothing marks where they stop — you have to read `g.p`. |
| Gradient alpha ramp | the same array, after `g.p * 4` | Pairs of `position, alpha`. This is the part everyone misses. Without it a gradient cannot be interpreted: a ramp reaching 0 is a mask dissolving into whatever is behind, not a shape with a colour. |
| Animated gradient | `g.k.k[i].s` | The whole ramp is keyframed, so `g.k.k` holds keyframe objects rather than numbers. Code that assumes numbers silently produces nothing. |
| Solid layer | layer `ty: 1` → `sc` | A `#rrggbb` string, not an array, and it sits on the layer rather than in the shape tree. Easy to miss entirely — there are 41 of them in the corpus this was built against. |
| Text | `t.d.k[i].s.fc` and `.sc` | Fill and stroke of a text document, animated per keyframe. Most "text" in exported files is converted to paths and is really just `fl`, but real text layers do exist. |
| Effect colour | layer `ef[].ef[]` with `ty: 2` → `v.k` | A Drop Shadow carries its own colour, and nothing in the layer’s shapes mentions it. No palette built from fills and strokes will ever show it — which is how a violet glow survives a dark-to-light conversion and lands as a pink halo on a white page. Addressed by path, not by slot index, so that finding it did not renumber every slot in every file already converted. |
| Raster image | `assets[].p` | A data URI when embedded, a filename when not. Dark bitmaps are why a converted animation still has dark patches. |
Slots, and why their order is a contract
Every colour above is addressed as a slot: a path into the document plus an offset. Slots are numbered by walking layers in z-order and descending into precomp assets where they are referenced. That order is the contract — it is what makes a saved colour map, a set of groups or a named theme still land on the right shapes after the file is exported and re-imported.
A slot is not an editable colour
A precomp referenced by ten layers is one JSON object walked ten times. Ten slots, one value: change it and all ten change, and no tool can give them different colours without splitting the asset. In one file of the corpus that is 110 slots collapsing to 11 colours you can actually edit — which is exactly how many lottie-web paints.
The gradient rule
If a gradient’s alpha ramp reaches zero, it is not a shape — it is a fade into whatever the animation sits on. Inverting its colours the way you would a surface leaves a dark halo on a light page. Its stops should take the colour of the new backdrop instead. This one rule accounts for most of what looks wrong in automatically converted animations.