--wip-- [skip ci]
diff --git a/issue-repro/src/main/res/layout/issue_repro_activity.xml b/issue-repro/src/main/res/layout/issue_repro_activity.xml
index 05ea4fb..13318c7 100755
--- a/issue-repro/src/main/res/layout/issue_repro_activity.xml
+++ b/issue-repro/src/main/res/layout/issue_repro_activity.xml
@@ -9,7 +9,8 @@
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:layout_gravity="center"
-        app:lottie_rawRes="@raw/heart"
+        app:lottie_rawRes="@raw/keypad2_camera"
+        android:background="#cccccc"
         app:lottie_autoPlay="true"
         app:lottie_loop="true"/>
 </FrameLayout>
\ No newline at end of file
diff --git a/issue-repro/src/main/res/raw/keypad.json b/issue-repro/src/main/res/raw/keypad.json
new file mode 100644
index 0000000..6f13fc9
--- /dev/null
+++ b/issue-repro/src/main/res/raw/keypad.json
@@ -0,0 +1,407 @@
+{
+  "v": "5.10.2",
+  "fr": 60,
+  "ip": 0,
+  "op": 300,
+  "w": 300,
+  "h": 500,
+  "nm": "Keypad 2",
+  "ddd": 1,
+  "assets": [],
+  "layers": [
+    {
+      "ddd": 1,
+      "ind": 1,
+      "ty": 4,
+      "nm": "Lock",
+      "parent": 2,
+      "sr": 1,
+      "ks": {
+        "o": {
+          "a": 0,
+          "k": 100,
+          "ix": 11
+        },
+        "rx": {
+          "a": 0,
+          "k": 0,
+          "ix": 8
+        },
+        "ry": {
+          "a": 0,
+          "k": 0,
+          "ix": 9
+        },
+        "rz": {
+          "a": 0,
+          "k": 0,
+          "ix": 10
+        },
+        "or": {
+          "a": 0,
+          "k": [
+            0,
+            0,
+            0
+          ],
+          "ix": 7
+        },
+        "p": {
+          "a": 0,
+          "k": [
+            0,
+            -1.363,
+            0
+          ],
+          "ix": 2
+        },
+        "a": {
+          "a": 0,
+          "k": [
+            2.781,
+            -87.363,
+            0
+          ],
+          "ix": 1
+        },
+        "s": {
+          "a": 0,
+          "k": [
+            100,
+            100,
+            100
+          ],
+          "ix": 6
+        }
+      },
+      "ao": 0,
+      "shapes": [
+        {
+          "ty": "gr",
+          "it": [
+            {
+              "ty": "rc",
+              "d": 1,
+              "s": {
+                "a": 0,
+                "k": [
+                  234.789,
+                  258.758
+                ],
+                "ix": 2
+              },
+              "p": {
+                "a": 0,
+                "k": [
+                  0,
+                  0
+                ],
+                "ix": 3
+              },
+              "r": {
+                "a": 0,
+                "k": 48,
+                "ix": 4
+              },
+              "nm": "Rectangle Path 1",
+              "mn": "ADBE Vector Shape - Rect",
+              "hd": false
+            },
+            {
+              "ty": "st",
+              "c": {
+                "a": 0,
+                "k": [
+                  0,
+                  0,
+                  0,
+                  1
+                ],
+                "ix": 3
+              },
+              "o": {
+                "a": 0,
+                "k": 100,
+                "ix": 4
+              },
+              "w": {
+                "a": 0,
+                "k": 4,
+                "ix": 5
+              },
+              "lc": 1,
+              "lj": 1,
+              "ml": 4,
+              "bm": 0,
+              "nm": "Stroke 1",
+              "mn": "ADBE Vector Graphic - Stroke",
+              "hd": false
+            },
+            {
+              "ty": "fl",
+              "c": {
+                "a": 0,
+                "k": [
+                  1,
+                  1,
+                  1,
+                  1
+                ],
+                "ix": 4
+              },
+              "o": {
+                "a": 0,
+                "k": 100,
+                "ix": 5
+              },
+              "r": 1,
+              "bm": 0,
+              "nm": "Fill 1",
+              "mn": "ADBE Vector Graphic - Fill",
+              "hd": false
+            },
+            {
+              "ty": "tr",
+              "p": {
+                "a": 0,
+                "k": [
+                  2.781,
+                  -87.363
+                ],
+                "ix": 2
+              },
+              "a": {
+                "a": 0,
+                "k": [
+                  0,
+                  0
+                ],
+                "ix": 1
+              },
+              "s": {
+                "a": 0,
+                "k": [
+                  100,
+                  100
+                ],
+                "ix": 3
+              },
+              "r": {
+                "a": 0,
+                "k": 0,
+                "ix": 6
+              },
+              "o": {
+                "a": 0,
+                "k": 100,
+                "ix": 7
+              },
+              "sk": {
+                "a": 0,
+                "k": 0,
+                "ix": 4
+              },
+              "sa": {
+                "a": 0,
+                "k": 0,
+                "ix": 5
+              },
+              "nm": "Transform"
+            }
+          ],
+          "nm": "Rectangle 1",
+          "np": 3,
+          "cix": 2,
+          "bm": 0,
+          "ix": 1,
+          "mn": "ADBE Vector Group",
+          "hd": false
+        }
+      ],
+      "ip": 0,
+      "op": 300,
+      "st": 0,
+      "ct": 1,
+      "bm": 0
+    },
+    {
+      "ddd": 1,
+      "ind": 2,
+      "ty": 3,
+      "nm": "Rotator",
+      "sr": 1,
+      "ks": {
+        "o": {
+          "a": 0,
+          "k": 0,
+          "ix": 11
+        },
+        "rx": {
+          "a": 0,
+          "k": 0,
+          "ix": 8
+        },
+        "ry": {
+          "a": 1,
+          "k": [
+            {
+              "i": {
+                "x": [
+                  0.8
+                ],
+                "y": [
+                  0.15
+                ]
+              },
+              "o": {
+                "x": [
+                  0.3
+                ],
+                "y": [
+                  0
+                ]
+              },
+              "t": 0,
+              "s": [
+                90
+              ]
+            },
+            {
+              "i": {
+                "x": [
+                  0.1
+                ],
+                "y": [
+                  1
+                ]
+              },
+              "o": {
+                "x": [
+                  0.05
+                ],
+                "y": [
+                  0.7
+                ]
+              },
+              "t": 25,
+              "s": [
+                54
+              ]
+            },
+            {
+              "t": 150,
+              "s": [
+                0
+              ]
+            }
+          ],
+          "ix": 9
+        },
+        "rz": {
+          "a": 0,
+          "k": 0,
+          "ix": 10
+        },
+        "or": {
+          "a": 0,
+          "k": [
+            0,
+            0,
+            0
+          ],
+          "ix": 7
+        },
+        "p": {
+          "a": 1,
+          "k": [
+            {
+              "i": {
+                "x": 0.8,
+                "y": 0.15
+              },
+              "o": {
+                "x": 0.3,
+                "y": 0
+              },
+              "t": 0,
+              "s": [
+                150,
+                363,
+                0
+              ],
+              "to": [
+                0,
+                -0.59,
+                0
+              ],
+              "ti": [
+                0,
+                10.148,
+                0
+              ]
+            },
+            {
+              "i": {
+                "x": 0.1,
+                "y": 1
+              },
+              "o": {
+                "x": 0.05,
+                "y": 0.7
+              },
+              "t": 25,
+              "s": [
+                150,
+                282.6,
+                0
+              ],
+              "to": [
+                0,
+                -46.488,
+                0
+              ],
+              "ti": [
+                0,
+                11.712,
+                0
+              ]
+            },
+            {
+              "t": 150,
+              "s": [
+                150,
+                162,
+                0
+              ]
+            }
+          ],
+          "ix": 2
+        },
+        "a": {
+          "a": 0,
+          "k": [
+            0,
+            0,
+            0
+          ],
+          "ix": 1
+        },
+        "s": {
+          "a": 0,
+          "k": [
+            100,
+            100,
+            100
+          ],
+          "ix": 6
+        }
+      },
+      "ao": 0,
+      "ip": 0,
+      "op": 300,
+      "st": 0,
+      "bm": 0
+    }
+  ],
+  "markers": []
+}
\ No newline at end of file
diff --git a/issue-repro/src/main/res/raw/keypad2.json b/issue-repro/src/main/res/raw/keypad2.json
new file mode 100644
index 0000000..ade8b3a
--- /dev/null
+++ b/issue-repro/src/main/res/raw/keypad2.json
@@ -0,0 +1,288 @@
+{
+  "v": "5.10.2",
+  "fr": 60,
+  "ip": 0,
+  "op": 171,
+  "w": 300,
+  "h": 500,
+  "nm": "Keypad 2",
+  "ddd": 1,
+  "assets": [],
+  "layers": [
+    {
+      "ddd": 1,
+      "ind": 1,
+      "ty": 4,
+      "nm": "Lock",
+      "sr": 1,
+      "ks": {
+        "o": {
+          "a": 0,
+          "k": 100,
+          "ix": 11
+        },
+        "rx": {
+          "a": 0,
+          "k": 0,
+          "ix": 8
+        },
+        "ry": {
+          "a": 1,
+          "k": [
+            {
+              "i": {
+                "x": [
+                  0.833
+                ],
+                "y": [
+                  0.833
+                ]
+              },
+              "o": {
+                "x": [
+                  0.167
+                ],
+                "y": [
+                  0.167
+                ]
+              },
+              "t": 0,
+              "s": [
+                90
+              ]
+            },
+            {
+              "t": 170,
+              "s": [
+                0
+              ]
+            }
+          ],
+          "ix": 9
+        },
+        "rz": {
+          "a": 0,
+          "k": 0,
+          "ix": 10
+        },
+        "or": {
+          "a": 0,
+          "k": [
+            0,
+            0,
+            0
+          ],
+          "ix": 7
+        },
+        "p": {
+          "a": 1,
+          "k": [
+            {
+              "i": {
+                "x": 0.833,
+                "y": 0.833
+              },
+              "o": {
+                "x": 0.167,
+                "y": 0.167
+              },
+              "t": 0,
+              "s": [
+                150,
+                447,
+                0
+              ],
+              "to": [
+                0,
+                -36.5,
+                0
+              ],
+              "ti": [
+                0,
+                36.5,
+                0
+              ]
+            },
+            {
+              "t": 170,
+              "s": [
+                150,
+                228,
+                0
+              ]
+            }
+          ],
+          "ix": 2
+        },
+        "a": {
+          "a": 0,
+          "k": [
+            0,
+            0,
+            0
+          ],
+          "ix": 1
+        },
+        "s": {
+          "a": 0,
+          "k": [
+            100,
+            100,
+            100
+          ],
+          "ix": 6
+        }
+      },
+      "ao": 0,
+      "shapes": [
+        {
+          "ty": "gr",
+          "it": [
+            {
+              "ty": "rc",
+              "d": 1,
+              "s": {
+                "a": 0,
+                "k": [
+                  234.789,
+                  258.758
+                ],
+                "ix": 2
+              },
+              "p": {
+                "a": 0,
+                "k": [
+                  0,
+                  0
+                ],
+                "ix": 3
+              },
+              "r": {
+                "a": 0,
+                "k": 48,
+                "ix": 4
+              },
+              "nm": "Rectangle Path 1",
+              "mn": "ADBE Vector Shape - Rect",
+              "hd": false
+            },
+            {
+              "ty": "st",
+              "c": {
+                "a": 0,
+                "k": [
+                  0,
+                  0,
+                  0,
+                  1
+                ],
+                "ix": 3
+              },
+              "o": {
+                "a": 0,
+                "k": 100,
+                "ix": 4
+              },
+              "w": {
+                "a": 0,
+                "k": 4,
+                "ix": 5
+              },
+              "lc": 1,
+              "lj": 1,
+              "ml": 4,
+              "bm": 0,
+              "nm": "Stroke 1",
+              "mn": "ADBE Vector Graphic - Stroke",
+              "hd": false
+            },
+            {
+              "ty": "fl",
+              "c": {
+                "a": 0,
+                "k": [
+                  1,
+                  1,
+                  1,
+                  1
+                ],
+                "ix": 4
+              },
+              "o": {
+                "a": 0,
+                "k": 100,
+                "ix": 5
+              },
+              "r": 1,
+              "bm": 0,
+              "nm": "Fill 1",
+              "mn": "ADBE Vector Graphic - Fill",
+              "hd": false
+            },
+            {
+              "ty": "tr",
+              "p": {
+                "a": 0,
+                "k": [
+                  2.781,
+                  -87.363
+                ],
+                "ix": 2
+              },
+              "a": {
+                "a": 0,
+                "k": [
+                  0,
+                  0
+                ],
+                "ix": 1
+              },
+              "s": {
+                "a": 0,
+                "k": [
+                  100,
+                  100
+                ],
+                "ix": 3
+              },
+              "r": {
+                "a": 0,
+                "k": 0,
+                "ix": 6
+              },
+              "o": {
+                "a": 0,
+                "k": 100,
+                "ix": 7
+              },
+              "sk": {
+                "a": 0,
+                "k": 0,
+                "ix": 4
+              },
+              "sa": {
+                "a": 0,
+                "k": 0,
+                "ix": 5
+              },
+              "nm": "Transform"
+            }
+          ],
+          "nm": "Rectangle 1",
+          "np": 3,
+          "cix": 2,
+          "bm": 0,
+          "ix": 1,
+          "mn": "ADBE Vector Group",
+          "hd": false
+        }
+      ],
+      "ip": 0,
+      "op": 171,
+      "st": 0,
+      "ct": 1,
+      "bm": 0
+    }
+  ],
+  "markers": []
+}
\ No newline at end of file
diff --git a/issue-repro/src/main/res/raw/keypad2_2d.json b/issue-repro/src/main/res/raw/keypad2_2d.json
new file mode 100644
index 0000000..edebe57
--- /dev/null
+++ b/issue-repro/src/main/res/raw/keypad2_2d.json
@@ -0,0 +1,243 @@
+{
+  "v": "5.10.2",
+  "fr": 60,
+  "ip": 0,
+  "op": 171,
+  "w": 300,
+  "h": 500,
+  "nm": "Keypad 2 2D",
+  "ddd": 0,
+  "assets": [],
+  "layers": [
+    {
+      "ddd": 0,
+      "ind": 1,
+      "ty": 4,
+      "nm": "Lock",
+      "sr": 1,
+      "ks": {
+        "o": {
+          "a": 0,
+          "k": 100,
+          "ix": 11
+        },
+        "r": {
+          "a": 0,
+          "k": 0,
+          "ix": 10
+        },
+        "p": {
+          "a": 1,
+          "k": [
+            {
+              "i": {
+                "x": 0.833,
+                "y": 0.833
+              },
+              "o": {
+                "x": 0.167,
+                "y": 0.167
+              },
+              "t": 0,
+              "s": [
+                150,
+                447,
+                0
+              ],
+              "to": [
+                0,
+                -36.5,
+                0
+              ],
+              "ti": [
+                0,
+                36.5,
+                0
+              ]
+            },
+            {
+              "t": 170,
+              "s": [
+                150,
+                228,
+                0
+              ]
+            }
+          ],
+          "ix": 2,
+          "l": 2
+        },
+        "a": {
+          "a": 0,
+          "k": [
+            0,
+            0,
+            0
+          ],
+          "ix": 1,
+          "l": 2
+        },
+        "s": {
+          "a": 0,
+          "k": [
+            100,
+            100,
+            100
+          ],
+          "ix": 6,
+          "l": 2
+        }
+      },
+      "ao": 0,
+      "shapes": [
+        {
+          "ty": "gr",
+          "it": [
+            {
+              "ty": "rc",
+              "d": 1,
+              "s": {
+                "a": 0,
+                "k": [
+                  234.789,
+                  258.758
+                ],
+                "ix": 2
+              },
+              "p": {
+                "a": 0,
+                "k": [
+                  0,
+                  0
+                ],
+                "ix": 3
+              },
+              "r": {
+                "a": 0,
+                "k": 48,
+                "ix": 4
+              },
+              "nm": "Rectangle Path 1",
+              "mn": "ADBE Vector Shape - Rect",
+              "hd": false
+            },
+            {
+              "ty": "st",
+              "c": {
+                "a": 0,
+                "k": [
+                  0,
+                  0,
+                  0,
+                  1
+                ],
+                "ix": 3
+              },
+              "o": {
+                "a": 0,
+                "k": 100,
+                "ix": 4
+              },
+              "w": {
+                "a": 0,
+                "k": 4,
+                "ix": 5
+              },
+              "lc": 1,
+              "lj": 1,
+              "ml": 4,
+              "bm": 0,
+              "nm": "Stroke 1",
+              "mn": "ADBE Vector Graphic - Stroke",
+              "hd": false
+            },
+            {
+              "ty": "fl",
+              "c": {
+                "a": 0,
+                "k": [
+                  1,
+                  1,
+                  1,
+                  1
+                ],
+                "ix": 4
+              },
+              "o": {
+                "a": 0,
+                "k": 100,
+                "ix": 5
+              },
+              "r": 1,
+              "bm": 0,
+              "nm": "Fill 1",
+              "mn": "ADBE Vector Graphic - Fill",
+              "hd": false
+            },
+            {
+              "ty": "tr",
+              "p": {
+                "a": 0,
+                "k": [
+                  2.781,
+                  -87.363
+                ],
+                "ix": 2
+              },
+              "a": {
+                "a": 0,
+                "k": [
+                  0,
+                  0
+                ],
+                "ix": 1
+              },
+              "s": {
+                "a": 0,
+                "k": [
+                  100,
+                  100
+                ],
+                "ix": 3
+              },
+              "r": {
+                "a": 0,
+                "k": 0,
+                "ix": 6
+              },
+              "o": {
+                "a": 0,
+                "k": 100,
+                "ix": 7
+              },
+              "sk": {
+                "a": 0,
+                "k": 0,
+                "ix": 4
+              },
+              "sa": {
+                "a": 0,
+                "k": 0,
+                "ix": 5
+              },
+              "nm": "Transform"
+            }
+          ],
+          "nm": "Rectangle 1",
+          "np": 3,
+          "cix": 2,
+          "bm": 0,
+          "ix": 1,
+          "mn": "ADBE Vector Group",
+          "hd": false
+        }
+      ],
+      "ip": 0,
+      "op": 171,
+      "st": 0,
+      "ct": 1,
+      "bm": 0
+    }
+  ],
+  "markers": []
+}
\ No newline at end of file
diff --git a/issue-repro/src/main/res/raw/keypad2_camera.json b/issue-repro/src/main/res/raw/keypad2_camera.json
new file mode 100644
index 0000000..9137cab
--- /dev/null
+++ b/issue-repro/src/main/res/raw/keypad2_camera.json
@@ -0,0 +1,421 @@
+{
+  "v": "5.10.2",
+  "fr": 60,
+  "ip": 0,
+  "op": 171,
+  "w": 300,
+  "h": 500,
+  "nm": "Keypad 2 Camera",
+  "ddd": 1,
+  "assets": [],
+  "layers": [
+    {
+      "ddd": 0,
+      "ind": 1,
+      "ty": 13,
+      "nm": "Camera 1",
+      "sr": 1,
+      "pe": {
+        "a": 0,
+        "k": 291.667,
+        "ix": 1
+      },
+      "ks": {
+        "p": {
+          "a": 1,
+          "k": [
+            {
+              "i": {
+                "x": 0.833,
+                "y": 0.833
+              },
+              "o": {
+                "x": 0.167,
+                "y": 0.167
+              },
+              "t": 0,
+              "s": [
+                150,
+                250,
+                -431.667
+              ],
+              "to": [
+                0,
+                0,
+                -112.5
+              ],
+              "ti": [
+                0,
+                0,
+                112.5
+              ]
+            },
+            {
+              "t": 170,
+              "s": [
+                150,
+                250,
+                -1106.667
+              ]
+            }
+          ],
+          "ix": 2
+        },
+        "or": {
+          "a": 0,
+          "k": [
+            0,
+            0,
+            0
+          ],
+          "ix": 7
+        },
+        "rx": {
+          "a": 0,
+          "k": 0,
+          "ix": 8
+        },
+        "ry": {
+          "a": 0,
+          "k": 0,
+          "ix": 9
+        },
+        "rz": {
+          "a": 0,
+          "k": 0,
+          "ix": 10
+        }
+      },
+      "ip": 0,
+      "op": 171,
+      "st": 0,
+      "bm": 0
+    },
+    {
+      "ddd": 1,
+      "ind": 2,
+      "ty": 4,
+      "nm": "Lock",
+      "sr": 1,
+      "ks": {
+        "o": {
+          "a": 0,
+          "k": 100,
+          "ix": 11
+        },
+        "rx": {
+          "a": 0,
+          "k": 0,
+          "ix": 8
+        },
+        "ry": {
+          "a": 0,
+          "k": 0,
+          "ix": 9
+        },
+        "rz": {
+          "a": 0,
+          "k": 0,
+          "ix": 10
+        },
+        "or": {
+          "a": 0,
+          "k": [
+            0,
+            0,
+            0
+          ],
+          "ix": 7
+        },
+        "p": {
+          "a": 0,
+          "k": [
+            0,
+            0,
+            0
+          ],
+          "ix": 2
+        },
+        "a": {
+          "a": 0,
+          "k": [
+            0,
+            0,
+            0
+          ],
+          "ix": 1
+        },
+        "s": {
+          "a": 0,
+          "k": [
+            100,
+            100,
+            100
+          ],
+          "ix": 6
+        }
+      },
+      "ao": 0,
+      "shapes": [
+        {
+          "ty": "gr",
+          "it": [
+            {
+              "ty": "rc",
+              "d": 1,
+              "s": {
+                "a": 0,
+                "k": [
+                  234.789,
+                  258.758
+                ],
+                "ix": 2
+              },
+              "p": {
+                "a": 0,
+                "k": [
+                  0,
+                  0
+                ],
+                "ix": 3
+              },
+              "r": {
+                "a": 0,
+                "k": 48,
+                "ix": 4
+              },
+              "nm": "Rectangle Path 1",
+              "mn": "ADBE Vector Shape - Rect",
+              "hd": false
+            },
+            {
+              "ty": "st",
+              "c": {
+                "a": 0,
+                "k": [
+                  0,
+                  0,
+                  0,
+                  1
+                ],
+                "ix": 3
+              },
+              "o": {
+                "a": 0,
+                "k": 100,
+                "ix": 4
+              },
+              "w": {
+                "a": 0,
+                "k": 4,
+                "ix": 5
+              },
+              "lc": 1,
+              "lj": 1,
+              "ml": 4,
+              "bm": 0,
+              "nm": "Stroke 1",
+              "mn": "ADBE Vector Graphic - Stroke",
+              "hd": false
+            },
+            {
+              "ty": "fl",
+              "c": {
+                "a": 0,
+                "k": [
+                  1,
+                  1,
+                  1,
+                  1
+                ],
+                "ix": 4
+              },
+              "o": {
+                "a": 0,
+                "k": 100,
+                "ix": 5
+              },
+              "r": 1,
+              "bm": 0,
+              "nm": "Fill 1",
+              "mn": "ADBE Vector Graphic - Fill",
+              "hd": false
+            },
+            {
+              "ty": "tr",
+              "p": {
+                "a": 0,
+                "k": [
+                  150,
+                  250
+                ],
+                "ix": 2
+              },
+              "a": {
+                "a": 0,
+                "k": [
+                  0,
+                  0
+                ],
+                "ix": 1
+              },
+              "s": {
+                "a": 0,
+                "k": [
+                  100,
+                  100
+                ],
+                "ix": 3
+              },
+              "r": {
+                "a": 0,
+                "k": 0,
+                "ix": 6
+              },
+              "o": {
+                "a": 0,
+                "k": 100,
+                "ix": 7
+              },
+              "sk": {
+                "a": 0,
+                "k": 0,
+                "ix": 4
+              },
+              "sa": {
+                "a": 0,
+                "k": 0,
+                "ix": 5
+              },
+              "nm": "Transform"
+            }
+          ],
+          "nm": "Rectangle 1",
+          "np": 3,
+          "cix": 2,
+          "bm": 0,
+          "ix": 1,
+          "mn": "ADBE Vector Group",
+          "hd": false
+        },
+        {
+          "ty": "gr",
+          "it": [
+            {
+              "ty": "rc",
+              "d": 1,
+              "s": {
+                "a": 0,
+                "k": [
+                  300,
+                  500
+                ],
+                "ix": 2
+              },
+              "p": {
+                "a": 0,
+                "k": [
+                  150,
+                  250
+                ],
+                "ix": 3
+              },
+              "r": {
+                "a": 0,
+                "k": 0,
+                "ix": 4
+              },
+              "nm": "Rectangle Path 1",
+              "mn": "ADBE Vector Shape - Rect",
+              "hd": false
+            },
+            {
+              "ty": "fl",
+              "c": {
+                "a": 0,
+                "k": [
+                  1,
+                  0,
+                  0,
+                  1
+                ],
+                "ix": 4
+              },
+              "o": {
+                "a": 0,
+                "k": 100,
+                "ix": 5
+              },
+              "r": 1,
+              "bm": 0,
+              "nm": "Fill 1",
+              "mn": "ADBE Vector Graphic - Fill",
+              "hd": false
+            },
+            {
+              "ty": "tr",
+              "p": {
+                "a": 0,
+                "k": [
+                  0,
+                  0
+                ],
+                "ix": 2
+              },
+              "a": {
+                "a": 0,
+                "k": [
+                  0,
+                  0
+                ],
+                "ix": 1
+              },
+              "s": {
+                "a": 0,
+                "k": [
+                  100,
+                  100
+                ],
+                "ix": 3
+              },
+              "r": {
+                "a": 0,
+                "k": 0,
+                "ix": 6
+              },
+              "o": {
+                "a": 0,
+                "k": 100,
+                "ix": 7
+              },
+              "sk": {
+                "a": 0,
+                "k": 0,
+                "ix": 4
+              },
+              "sa": {
+                "a": 0,
+                "k": 0,
+                "ix": 5
+              },
+              "nm": "Transform"
+            }
+          ],
+          "nm": "BG",
+          "np": 2,
+          "cix": 2,
+          "bm": 0,
+          "ix": 2,
+          "mn": "ADBE Vector Group",
+          "hd": false
+        }
+      ],
+      "ip": 0,
+      "op": 171,
+      "st": 0,
+      "ct": 1,
+      "bm": 0
+    }
+  ],
+  "markers": []
+}
\ No newline at end of file
diff --git a/lottie/src/main/java/com/airbnb/lottie/animation/Point3F.java b/lottie/src/main/java/com/airbnb/lottie/animation/Point3F.java
new file mode 100644
index 0000000..d1e0ed7
--- /dev/null
+++ b/lottie/src/main/java/com/airbnb/lottie/animation/Point3F.java
@@ -0,0 +1,63 @@
+package com.airbnb.lottie.animation;
+
+public class Point3F {
+  public float x;
+  public float y;
+  public float z;
+
+  public Point3F() {
+  }
+
+  public Point3F(float x, float y, float z) {
+    this.x = x;
+    this.y = y;
+    this.z = z;
+  }
+
+  public void set(float x, float y, float z) {
+    this.x = x;
+    this.y = y;
+    this.z = z;
+  }
+
+  /**
+   * Returns true if the point's coordinates equal (x,y)
+   */
+  public final boolean equals(float x, float y, float z) {
+    return this.x == x && this.y == y && this.z == z;
+  }
+
+  @Override
+  public boolean equals(Object o) {
+    if (this == o) {
+      return true;
+    }
+    if (o == null || getClass() != o.getClass()) {
+      return false;
+    }
+
+    Point3F point3F = (Point3F) o;
+
+    if (Float.compare(point3F.x, x) != 0) {
+      return false;
+    }
+    if (Float.compare(point3F.y, y) != 0) {
+      return false;
+    }
+    return Float.compare(point3F.z, z) == 0;
+  }
+
+  @Override
+  public int hashCode() {
+    int result = (x != 0.0f ? Float.floatToIntBits(x) : 0);
+    result = 31 * result + (y != 0.0f ? Float.floatToIntBits(y) : 0);
+    result = 31 * result + (z != 0.0f ? Float.floatToIntBits(z) : 0);
+    return result;
+  }
+
+  @Override
+  public String toString() {
+    return "PointF(" + x + ", " + y + ", " + z + ")";
+  }
+}
+
diff --git a/lottie/src/main/java/com/airbnb/lottie/animation/keyframe/CameraTransformKeyframeAnimation.java b/lottie/src/main/java/com/airbnb/lottie/animation/keyframe/CameraTransformKeyframeAnimation.java
new file mode 100644
index 0000000..cd69de8
--- /dev/null
+++ b/lottie/src/main/java/com/airbnb/lottie/animation/keyframe/CameraTransformKeyframeAnimation.java
@@ -0,0 +1,106 @@
+package com.airbnb.lottie.animation.keyframe;
+
+import android.graphics.Camera;
+import android.graphics.Matrix;
+import android.graphics.Rect;
+import android.util.Log;
+
+import androidx.annotation.Nullable;
+
+import com.airbnb.lottie.LottieComposition;
+import com.airbnb.lottie.animation.Point3F;
+import com.airbnb.lottie.model.animatable.AnimatableCameraTransform;
+import com.airbnb.lottie.model.animatable.AnimatablePoint3Value;
+import com.airbnb.lottie.model.layer.BaseLayer;
+
+public class CameraTransformKeyframeAnimation {
+  @Nullable private final BaseKeyframeAnimation<Point3F, Point3F> pointOfInterest;
+  private final BaseKeyframeAnimation<Point3F, Point3F> position;
+  private final BaseKeyframeAnimation<Point3F, Point3F> orientation;
+  private final BaseKeyframeAnimation<Float, Float> rotationX;
+  private final BaseKeyframeAnimation<Float, Float> rotationY;
+  private final BaseKeyframeAnimation<Float, Float> rotationZ;
+
+  private final Camera camera = new Camera();
+  private final LottieComposition composition;
+  private final Matrix matrix = new Matrix();
+
+
+  public CameraTransformKeyframeAnimation(LottieComposition composition, AnimatableCameraTransform animatableCameraTransform) {
+    this.composition = composition;
+    AnimatablePoint3Value poi = animatableCameraTransform.getPointOfInterest();
+    pointOfInterest = poi == null ? null : poi.createAnimation();
+    position = animatableCameraTransform.getPosition().createAnimation();
+    orientation = animatableCameraTransform.getOrientation().createAnimation();
+    rotationX = animatableCameraTransform.getRotationX().createAnimation();
+    rotationY = animatableCameraTransform.getRotationY().createAnimation();
+    rotationZ = animatableCameraTransform.getRotationZ().createAnimation();
+    camera.save();
+  }
+
+  public void addAnimationsToLayer(BaseLayer layer) {
+    layer.addAnimation(pointOfInterest);
+    layer.addAnimation(position);
+    layer.addAnimation(orientation);
+    layer.addAnimation(rotationX);
+    layer.addAnimation(rotationY);
+    layer.addAnimation(rotationZ);
+  }
+
+  public void addListener(BaseKeyframeAnimation.AnimationListener listener) {
+    if (pointOfInterest != null) {
+      pointOfInterest.addUpdateListener(listener);
+    }
+    position.addUpdateListener(listener);
+    orientation.addUpdateListener(listener);
+    rotationX.addUpdateListener(listener);
+    rotationY.addUpdateListener(listener);
+    rotationZ.addUpdateListener(listener);
+  }
+
+  public void setProgress(float progress) {
+    if (pointOfInterest != null) {
+      pointOfInterest.setProgress(progress);
+    }
+    position.setProgress(progress);
+    orientation.setProgress(progress);
+    rotationX.setProgress(progress);
+    rotationY.setProgress(progress);
+    rotationZ.setProgress(progress);
+  }
+
+  public Matrix getMatrix() {
+    // Is this the best way to reset a camera?
+    camera.restore();
+    camera.save();
+
+    Point3F position = this.position.getValue();
+    Rect bounds = composition.getBounds();
+    float originX = -bounds.width() / 2f;
+    float originY = -bounds.height() / 2f;
+    float lx = originX + position.x;
+    float ly = originY + position.y;
+
+    camera.setLocation(-1, 1, -8f);
+    camera.translate(0f, 0f, 200f);
+    // camera.translate(-originX, originY, 0f);
+
+    Point3F orientation = this.orientation.getValue();
+    float rx = this.rotationX.getValue() + orientation.x;
+    camera.rotateX(rx);
+    float ry = this.rotationY.getValue() + orientation.y;
+    camera.rotateY(ry);
+    float rz = this.rotationZ.getValue() + orientation.z;
+    camera.rotateZ(rz);
+
+    camera.getMatrix(matrix);
+    matrix.preTranslate(-lx, -ly);
+    matrix.postTranslate(-lx, -ly);
+
+    Log.d("Gabe", String.format("lx %.2f ly %.2f", lx, ly));
+
+    // TODO: point of interest.
+
+    return matrix;
+  }
+}
diff --git a/lottie/src/main/java/com/airbnb/lottie/animation/keyframe/Point3KeyframeAnimation.java b/lottie/src/main/java/com/airbnb/lottie/animation/keyframe/Point3KeyframeAnimation.java
new file mode 100644
index 0000000..4c08bb7
--- /dev/null
+++ b/lottie/src/main/java/com/airbnb/lottie/animation/keyframe/Point3KeyframeAnimation.java
@@ -0,0 +1,48 @@
+package com.airbnb.lottie.animation.keyframe;
+
+import com.airbnb.lottie.animation.Point3F;
+import com.airbnb.lottie.value.Keyframe;
+
+import java.util.List;
+
+public class Point3KeyframeAnimation extends KeyframeAnimation<Point3F> {
+  private final Point3F point = new Point3F();
+
+  public Point3KeyframeAnimation(List<Keyframe<Point3F>> keyframes) {
+    super(keyframes);
+  }
+
+  @Override public Point3F getValue(Keyframe<Point3F> keyframe, float keyframeProgress) {
+    return getValue(keyframe, keyframeProgress, keyframeProgress, keyframeProgress);
+  }
+
+  /**
+   * TODO: split keyframes doesn't really make sense with Point3F animations.
+   * Lottie currently only supports split path with separate X and Y animations
+   * but Point3F has a Z dimension.
+   * This may not matter because Point3F is only used for camera layers.
+   */
+  @Override protected Point3F getValue(Keyframe<Point3F> keyframe, float linearKeyframeProgress,
+      float xKeyframeProgress, float yKeyframeProgress) {
+    if (keyframe.startValue == null || keyframe.endValue == null) {
+      throw new IllegalStateException("Missing values for keyframe.");
+    }
+
+    Point3F startPoint = keyframe.startValue;
+    Point3F endPoint = keyframe.endValue;
+
+    if (valueCallback != null) {
+      //noinspection ConstantConditions
+      Point3F value = valueCallback.getValueInternal(keyframe.startFrame, keyframe.endFrame, startPoint,
+          endPoint, linearKeyframeProgress, getLinearCurrentKeyframeProgress(), getProgress());
+      if (value != null) {
+        return value;
+      }
+    }
+
+    point.set(startPoint.x + xKeyframeProgress * (endPoint.x - startPoint.x),
+        startPoint.y + yKeyframeProgress * (endPoint.y - startPoint.y),
+        startPoint.z + yKeyframeProgress * (endPoint.z - startPoint.z));
+    return point;
+  }
+}
\ No newline at end of file
diff --git a/lottie/src/main/java/com/airbnb/lottie/model/animatable/AnimatableCameraTransform.java b/lottie/src/main/java/com/airbnb/lottie/model/animatable/AnimatableCameraTransform.java
new file mode 100644
index 0000000..40ac29d
--- /dev/null
+++ b/lottie/src/main/java/com/airbnb/lottie/model/animatable/AnimatableCameraTransform.java
@@ -0,0 +1,48 @@
+package com.airbnb.lottie.model.animatable;
+
+import androidx.annotation.Nullable;
+
+public class AnimatableCameraTransform {
+  @Nullable private final AnimatablePoint3Value pointOfInterest;
+  private final AnimatablePoint3Value position;
+  private final AnimatablePoint3Value orientation;
+  private final AnimatableFloatValue rotationX;
+  private final AnimatableFloatValue rotationY;
+  private final AnimatableFloatValue rotationZ;
+
+  public AnimatableCameraTransform(@Nullable AnimatablePoint3Value pointOfInterest, AnimatablePoint3Value position,
+      AnimatablePoint3Value orientation, AnimatableFloatValue rotationX, AnimatableFloatValue rotationY,
+      AnimatableFloatValue rotationZ) {
+    this.pointOfInterest = pointOfInterest;
+    this.position = position;
+    this.orientation = orientation;
+    this.rotationX = rotationX;
+    this.rotationY = rotationY;
+    this.rotationZ = rotationZ;
+  }
+
+  @Nullable
+  public AnimatablePoint3Value getPointOfInterest() {
+    return pointOfInterest;
+  }
+
+  public AnimatablePoint3Value getPosition() {
+    return position;
+  }
+
+  public AnimatablePoint3Value getOrientation() {
+    return orientation;
+  }
+
+  public AnimatableFloatValue getRotationX() {
+    return rotationX;
+  }
+
+  public AnimatableFloatValue getRotationY() {
+    return rotationY;
+  }
+
+  public AnimatableFloatValue getRotationZ() {
+    return rotationZ;
+  }
+}
diff --git a/lottie/src/main/java/com/airbnb/lottie/model/animatable/AnimatablePoint3Value.java b/lottie/src/main/java/com/airbnb/lottie/model/animatable/AnimatablePoint3Value.java
new file mode 100644
index 0000000..fbe0f6d
--- /dev/null
+++ b/lottie/src/main/java/com/airbnb/lottie/model/animatable/AnimatablePoint3Value.java
@@ -0,0 +1,18 @@
+package com.airbnb.lottie.model.animatable;
+
+import com.airbnb.lottie.animation.Point3F;
+import com.airbnb.lottie.animation.keyframe.BaseKeyframeAnimation;
+import com.airbnb.lottie.animation.keyframe.Point3KeyframeAnimation;
+import com.airbnb.lottie.value.Keyframe;
+
+import java.util.List;
+
+public class AnimatablePoint3Value extends BaseAnimatableValue<Point3F, Point3F> {
+  public AnimatablePoint3Value(List<Keyframe<Point3F>> keyframes) {
+    super(keyframes);
+  }
+
+  @Override public BaseKeyframeAnimation<Point3F, Point3F> createAnimation() {
+    return new Point3KeyframeAnimation(keyframes);
+  }
+}
diff --git a/lottie/src/main/java/com/airbnb/lottie/model/layer/BaseLayer.java b/lottie/src/main/java/com/airbnb/lottie/model/layer/BaseLayer.java
index 5775c18..b7bd330 100644
--- a/lottie/src/main/java/com/airbnb/lottie/model/layer/BaseLayer.java
+++ b/lottie/src/main/java/com/airbnb/lottie/model/layer/BaseLayer.java
@@ -67,6 +67,8 @@
         return new NullLayer(drawable, layerModel);
       case TEXT:
         return new TextLayer(drawable, layerModel);
+      case CAMERA:
+        return new CameraLayer(drawable, layerModel, compositionLayer);
       case UNKNOWN:
       default:
         // Do nothing
@@ -162,7 +164,7 @@
     invalidateSelf();
   }
 
-  Layer getLayerModel() {
+  public Layer getLayerModel() {
     return layerModel;
   }
 
diff --git a/lottie/src/main/java/com/airbnb/lottie/model/layer/CameraLayer.java b/lottie/src/main/java/com/airbnb/lottie/model/layer/CameraLayer.java
new file mode 100644
index 0000000..719b08f
--- /dev/null
+++ b/lottie/src/main/java/com/airbnb/lottie/model/layer/CameraLayer.java
@@ -0,0 +1,39 @@
+package com.airbnb.lottie.model.layer;
+
+import android.graphics.Canvas;
+import android.graphics.Matrix;
+
+import com.airbnb.lottie.LottieDrawable;
+import com.airbnb.lottie.animation.keyframe.CameraTransformKeyframeAnimation;
+
+public class CameraLayer extends BaseLayer {
+
+  private final CompositionLayer compositionLayer;
+  private final CameraTransformKeyframeAnimation cameraTransform;
+  // private final Matrix cameraMatrix = new Matrix();
+  // private final Matrix parentAndCameraMatrix = new Matrix();
+
+  CameraLayer(LottieDrawable lottieDrawable, Layer layerModel, CompositionLayer compositionLayer) {
+    super(lottieDrawable, layerModel);
+    //noinspection DataFlowIssue
+    cameraTransform = new CameraTransformKeyframeAnimation(compositionLayer.getLayerModel().getComposition(), layerModel.getCameraTransform());
+    cameraTransform.addAnimationsToLayer(this);
+    cameraTransform.addListener(this);
+    this.compositionLayer = compositionLayer;
+  }
+
+  @Override void setProgress(float progress) {
+    super.setProgress(progress);
+    cameraTransform.setProgress(progress);
+  }
+
+  @Override void drawLayer(Canvas canvas, Matrix parentMatrix, int parentAlpha) {
+    Matrix cameraMatrix = cameraTransform.getMatrix();
+    // parentAndCameraMatrix.set(parentMatrix);
+    // parentAndCameraMatrix.preConcat(cameraMatrix);
+    canvas.save();
+    canvas.concat(cameraMatrix);
+    compositionLayer.drawLayerInternal(canvas, parentMatrix, parentAlpha);
+    canvas.restore();
+  }
+}
diff --git a/lottie/src/main/java/com/airbnb/lottie/model/layer/CompositionLayer.java b/lottie/src/main/java/com/airbnb/lottie/model/layer/CompositionLayer.java
index 7c923a6..327d979 100644
--- a/lottie/src/main/java/com/airbnb/lottie/model/layer/CompositionLayer.java
+++ b/lottie/src/main/java/com/airbnb/lottie/model/layer/CompositionLayer.java
@@ -4,7 +4,6 @@
 import android.graphics.Matrix;
 import android.graphics.Paint;
 import android.graphics.RectF;
-import android.util.Log;
 
 import androidx.annotation.FloatRange;
 import androidx.annotation.Nullable;
@@ -27,6 +26,7 @@
 public class CompositionLayer extends BaseLayer {
   @Nullable private BaseKeyframeAnimation<Float, Float> timeRemapping;
   private final List<BaseLayer> layers = new ArrayList<>();
+  private final List<CameraLayer> cameraLayers = new ArrayList<>();
   private final RectF rect = new RectF();
   private final RectF newClipRect = new RectF();
   private final Paint layerPaint = new Paint();
@@ -60,6 +60,9 @@
       BaseLayer layer = BaseLayer.forModel(this, lm, lottieDrawable, composition);
       if (layer == null) {
         continue;
+      } else if (layer instanceof CameraLayer) {
+        cameraLayers.add((CameraLayer) layer);
+        continue;
       }
       layerMap.put(layer.getLayerModel().getId(), layer);
       if (mattedLayer != null) {
@@ -104,6 +107,15 @@
   }
 
   @Override void drawLayer(Canvas canvas, Matrix parentMatrix, int parentAlpha) {
+    if (cameraLayers.size() > 0) {
+      cameraLayers.get(0).drawLayer(canvas, parentMatrix, parentAlpha);
+    } else {
+      drawLayerInternal(canvas, parentMatrix, parentAlpha);
+    }
+  }
+
+
+  void drawLayerInternal(Canvas canvas, Matrix parentMatrix, int parentAlpha) {
     L.beginSection("CompositionLayer#draw");
     newClipRect.set(0, 0, layerModel.getPreCompWidth(), layerModel.getPreCompHeight());
     parentMatrix.mapRect(newClipRect);
@@ -166,6 +178,9 @@
     for (int i = layers.size() - 1; i >= 0; i--) {
       layers.get(i).setProgress(progress);
     }
+    for (int i = cameraLayers.size() - 1; i >= 0; i--) {
+      cameraLayers.get(i).setProgress(progress);
+    }
     L.endSection("CompositionLayer#setProgress");
   }
 
diff --git a/lottie/src/main/java/com/airbnb/lottie/model/layer/Layer.java b/lottie/src/main/java/com/airbnb/lottie/model/layer/Layer.java
index 3358661..67146a7 100644
--- a/lottie/src/main/java/com/airbnb/lottie/model/layer/Layer.java
+++ b/lottie/src/main/java/com/airbnb/lottie/model/layer/Layer.java
@@ -3,6 +3,7 @@
 import androidx.annotation.Nullable;
 
 import com.airbnb.lottie.LottieComposition;
+import com.airbnb.lottie.model.animatable.AnimatableCameraTransform;
 import com.airbnb.lottie.model.animatable.AnimatableFloatValue;
 import com.airbnb.lottie.model.animatable.AnimatableTextFrame;
 import com.airbnb.lottie.model.animatable.AnimatableTextProperties;
@@ -19,13 +20,20 @@
 public class Layer {
 
   public enum LayerType {
-    PRE_COMP,
-    SOLID,
-    IMAGE,
-    NULL,
-    SHAPE,
-    TEXT,
-    UNKNOWN
+    PRE_COMP, // 0
+    SOLID,    // 1
+    IMAGE,    // 2
+    NULL,     // 3
+    SHAPE,    // 4
+    TEXT,     // 5
+    UNKNOWN,  // 6
+    UNUSED1,  // 7
+    UNUSED2,  // 8
+    UNUSED3,  // 9
+    UNUSED4,  // 10
+    UNUSED5,  // 11
+    UNUSED6,  // 12
+    CAMERA,   // 13
   }
 
   public enum MatteType {
@@ -61,6 +69,10 @@
   private final boolean hidden;
   @Nullable private final BlurEffect blurEffect;
   @Nullable private final DropShadowEffect dropShadowEffect;
+  /**
+   * Only set for Camera layers.
+   */
+  @Nullable private final AnimatableCameraTransform cameraTransform;
 
   public Layer(List<ContentModel> shapes, LottieComposition composition, String layerName, long layerId,
       LayerType layerType, long parentId, @Nullable String refId, List<Mask> masks,
@@ -69,7 +81,7 @@
       @Nullable AnimatableTextFrame text, @Nullable AnimatableTextProperties textProperties,
       List<Keyframe<Float>> inOutKeyframes, MatteType matteType,
       @Nullable AnimatableFloatValue timeRemapping, boolean hidden, @Nullable BlurEffect blurEffect,
-      @Nullable DropShadowEffect dropShadowEffect) {
+      @Nullable DropShadowEffect dropShadowEffect, @Nullable AnimatableCameraTransform cameraTransform) {
     this.shapes = shapes;
     this.composition = composition;
     this.layerName = layerName;
@@ -94,6 +106,7 @@
     this.hidden = hidden;
     this.blurEffect = blurEffect;
     this.dropShadowEffect = dropShadowEffect;
+    this.cameraTransform = cameraTransform;
   }
 
   LottieComposition getComposition() {
@@ -168,6 +181,10 @@
     return solidWidth;
   }
 
+  @Nullable public AnimatableCameraTransform getCameraTransform() {
+    return cameraTransform;
+  }
+
   @Nullable AnimatableTextFrame getText() {
     return text;
   }
diff --git a/lottie/src/main/java/com/airbnb/lottie/parser/AnimatableCameraTransformParser.java b/lottie/src/main/java/com/airbnb/lottie/parser/AnimatableCameraTransformParser.java
new file mode 100644
index 0000000..3cc077f
--- /dev/null
+++ b/lottie/src/main/java/com/airbnb/lottie/parser/AnimatableCameraTransformParser.java
@@ -0,0 +1,71 @@
+package com.airbnb.lottie.parser;
+
+import com.airbnb.lottie.LottieComposition;
+import com.airbnb.lottie.model.animatable.AnimatableCameraTransform;
+import com.airbnb.lottie.model.animatable.AnimatableFloatValue;
+import com.airbnb.lottie.model.animatable.AnimatablePoint3Value;
+import com.airbnb.lottie.parser.moshi.JsonReader;
+
+import java.io.IOException;
+
+public class AnimatableCameraTransformParser {
+
+  private AnimatableCameraTransformParser() {
+  }
+
+
+  private static final JsonReader.Options NAMES = JsonReader.Options.of(
+      "a",  // 0
+      "p",  // 1
+      "or", // 2
+      "rx", // 3
+      "ry", // 4
+      "rz"  // 5
+  );
+  private static final JsonReader.Options ANIMATABLE_NAMES = JsonReader.Options.of("k");
+
+  public static AnimatableCameraTransform parse(
+      JsonReader reader, LottieComposition composition) throws IOException {
+    AnimatablePoint3Value pointOfInterest = null;
+    AnimatablePoint3Value position = null;
+    AnimatablePoint3Value orientation = null;
+    AnimatableFloatValue rotationX = null;
+    AnimatableFloatValue rotationY = null;
+    AnimatableFloatValue rotationZ = null;
+
+    boolean isObject = reader.peek() == JsonReader.Token.BEGIN_OBJECT;
+    if (isObject) {
+      reader.beginObject();
+    }
+    while (reader.hasNext()) {
+      switch (reader.selectName(NAMES)) {
+        case 0: // a
+          pointOfInterest = AnimatableValueParser.parsePoint3(reader, composition, true);
+          break;
+        case 1: // p
+          position = AnimatableValueParser.parsePoint3(reader, composition, true);
+          break;
+        case 2: // or
+          orientation = AnimatableValueParser.parsePoint3(reader, composition, false);
+          break;
+        case 3: // rx
+          rotationX = AnimatableValueParser.parseFloat(reader, composition, false);
+          break;
+        case 4: // ry
+          rotationY = AnimatableValueParser.parseFloat(reader, composition, false);
+          break;
+        case 5: // rz
+          rotationZ = AnimatableValueParser.parseFloat(reader, composition, false);
+          break;
+        default:
+          reader.skipName();
+          reader.skipValue();
+      }
+    }
+    if (isObject) {
+      reader.endObject();
+    }
+
+    return new AnimatableCameraTransform(pointOfInterest, position, orientation ,rotationX, rotationY, rotationZ);
+  }
+}
diff --git a/lottie/src/main/java/com/airbnb/lottie/parser/AnimatableValueParser.java b/lottie/src/main/java/com/airbnb/lottie/parser/AnimatableValueParser.java
index f4b9eaa..c584a3e 100644
--- a/lottie/src/main/java/com/airbnb/lottie/parser/AnimatableValueParser.java
+++ b/lottie/src/main/java/com/airbnb/lottie/parser/AnimatableValueParser.java
@@ -6,6 +6,7 @@
 import com.airbnb.lottie.model.animatable.AnimatableFloatValue;
 import com.airbnb.lottie.model.animatable.AnimatableGradientColorValue;
 import com.airbnb.lottie.model.animatable.AnimatableIntegerValue;
+import com.airbnb.lottie.model.animatable.AnimatablePoint3Value;
 import com.airbnb.lottie.model.animatable.AnimatablePointValue;
 import com.airbnb.lottie.model.animatable.AnimatableScaleValue;
 import com.airbnb.lottie.model.animatable.AnimatableShapeValue;
@@ -42,6 +43,11 @@
     return new AnimatablePointValue(KeyframesParser.parse(reader, composition, Utils.dpScale(), PointFParser.INSTANCE, true));
   }
 
+  static AnimatablePoint3Value parsePoint3(
+      JsonReader reader, LottieComposition composition, boolean isDp) throws IOException {
+    return new AnimatablePoint3Value(KeyframesParser.parse(reader, composition, isDp ? Utils.dpScale() : 1f, Point3FParser.INSTANCE, false));
+  }
+
   static AnimatableScaleValue parseScale(
       JsonReader reader, LottieComposition composition) throws IOException {
     return new AnimatableScaleValue(parse(reader, composition, ScaleXYParser.INSTANCE));
diff --git a/lottie/src/main/java/com/airbnb/lottie/parser/JsonUtils.java b/lottie/src/main/java/com/airbnb/lottie/parser/JsonUtils.java
index d33ff8b..c104021 100644
--- a/lottie/src/main/java/com/airbnb/lottie/parser/JsonUtils.java
+++ b/lottie/src/main/java/com/airbnb/lottie/parser/JsonUtils.java
@@ -5,6 +5,7 @@
 
 import androidx.annotation.ColorInt;
 
+import com.airbnb.lottie.animation.Point3F;
 import com.airbnb.lottie.parser.moshi.JsonReader;
 
 import java.io.IOException;
@@ -101,6 +102,71 @@
     return new PointF(x * scale, y * scale);
   }
 
+  static Point3F jsonToPoint3(JsonReader reader, float scale) throws IOException {
+    switch (reader.peek()) {
+      case NUMBER:
+        return jsonNumbersToPoint3(reader, scale);
+      case BEGIN_ARRAY:
+        return jsonArrayToPoint3(reader, scale);
+      case BEGIN_OBJECT:
+        return jsonObjectToPoint3(reader, scale);
+      default:
+        throw new IllegalArgumentException("Unknown point starts with " + reader.peek());
+    }
+  }
+
+  private static Point3F jsonNumbersToPoint3(JsonReader reader, float scale) throws IOException {
+    float x = (float) reader.nextDouble();
+    float y = (float) reader.nextDouble();
+    float z = (float) reader.nextDouble();
+    while (reader.hasNext()) {
+      reader.skipValue();
+    }
+    return new Point3F(x * scale, y * scale, z * scale);
+  }
+
+  private static Point3F jsonArrayToPoint3(JsonReader reader, float scale) throws IOException {
+    float x;
+    float y;
+    float z;
+    reader.beginArray();
+    x = (float) reader.nextDouble();
+    y = (float) reader.nextDouble();
+    z = (float) reader.nextDouble();
+    while (reader.peek() != JsonReader.Token.END_ARRAY) {
+      reader.skipValue();
+    }
+    reader.endArray();
+    return new Point3F(x * scale, y * scale, z * scale);
+  }
+
+  private static final JsonReader.Options POINT3_NAMES = JsonReader.Options.of("x", "y", "z");
+
+  private static Point3F jsonObjectToPoint3(JsonReader reader, float scale) throws IOException {
+    float x = 0f;
+    float y = 0f;
+    float z = 0f;
+    reader.beginObject();
+    while (reader.hasNext()) {
+      switch (reader.selectName(POINT3_NAMES)) {
+        case 0:
+          x = valueFromObject(reader);
+          break;
+        case 1:
+          y = valueFromObject(reader);
+          break;
+        case 2:
+          z = valueFromObject(reader);
+          break;
+        default:
+          reader.skipName();
+          reader.skipValue();
+      }
+    }
+    reader.endObject();
+    return new Point3F(x * scale, y * scale, z * scale);
+  }
+
   static float valueFromObject(JsonReader reader) throws IOException {
     JsonReader.Token token = reader.peek();
     switch (token) {
diff --git a/lottie/src/main/java/com/airbnb/lottie/parser/LayerParser.java b/lottie/src/main/java/com/airbnb/lottie/parser/LayerParser.java
index e065cf0..fbf6d84 100644
--- a/lottie/src/main/java/com/airbnb/lottie/parser/LayerParser.java
+++ b/lottie/src/main/java/com/airbnb/lottie/parser/LayerParser.java
@@ -4,6 +4,7 @@
 import android.graphics.Rect;
 
 import com.airbnb.lottie.LottieComposition;
+import com.airbnb.lottie.model.animatable.AnimatableCameraTransform;
 import com.airbnb.lottie.model.animatable.AnimatableFloatValue;
 import com.airbnb.lottie.model.animatable.AnimatableTextFrame;
 import com.airbnb.lottie.model.animatable.AnimatableTextProperties;
@@ -59,7 +60,7 @@
         Layer.LayerType.PRE_COMP, -1, null, Collections.<Mask>emptyList(),
         new AnimatableTransform(), 0, 0, 0, 0, 0,
         bounds.width(), bounds.height(), null, null, Collections.<Keyframe<Float>>emptyList(),
-        Layer.MatteType.NONE, null, false, null, null);
+        Layer.MatteType.NONE, null, false, null, null, null);
   }
 
   private static final JsonReader.Options TEXT_NAMES = JsonReader.Options.of(
@@ -96,6 +97,7 @@
 
     Layer.MatteType matteType = Layer.MatteType.NONE;
     AnimatableTransform transform = null;
+    AnimatableCameraTransform cameraTransform = null;
     AnimatableTextFrame text = null;
     AnimatableTextProperties textProperties = null;
     AnimatableFloatValue timeRemapping = null;
@@ -117,7 +119,7 @@
           break;
         case 3:
           int layerTypeInt = reader.nextInt();
-          if (layerTypeInt < Layer.LayerType.UNKNOWN.ordinal()) {
+          if (layerTypeInt < Layer.LayerType.UNKNOWN.ordinal() || layerTypeInt == Layer.LayerType.CAMERA.ordinal()) {
             layerType = Layer.LayerType.values()[layerTypeInt];
           } else {
             layerType = Layer.LayerType.UNKNOWN;
@@ -136,7 +138,12 @@
           solidColor = Color.parseColor(reader.nextString());
           break;
         case 8:
-          transform = AnimatableTransformParser.parse(reader, composition);
+          if (layerType == Layer.LayerType.CAMERA) {
+            transform = new AnimatableTransform();
+            cameraTransform = AnimatableCameraTransformParser.parse(reader, composition);
+          } else {
+            transform = AnimatableTransformParser.parse(reader, composition);
+          }
           break;
         case 9:
           int matteTypeIndex = reader.nextInt();
@@ -287,6 +294,6 @@
     return new Layer(shapes, composition, layerName, layerId, layerType, parentId, refId,
         masks, transform, solidWidth, solidHeight, solidColor, timeStretch, startFrame,
         preCompWidth, preCompHeight, text, textProperties, inOutKeyframes, matteType,
-        timeRemapping, hidden, blurEffect, dropShadowEffect);
+        timeRemapping, hidden, blurEffect, dropShadowEffect, cameraTransform);
   }
 }
diff --git a/lottie/src/main/java/com/airbnb/lottie/parser/Point3FParser.java b/lottie/src/main/java/com/airbnb/lottie/parser/Point3FParser.java
new file mode 100644
index 0000000..6170504
--- /dev/null
+++ b/lottie/src/main/java/com/airbnb/lottie/parser/Point3FParser.java
@@ -0,0 +1,35 @@
+package com.airbnb.lottie.parser;
+
+import com.airbnb.lottie.animation.Point3F;
+import com.airbnb.lottie.parser.moshi.JsonReader;
+
+import java.io.IOException;
+
+public class Point3FParser implements ValueParser<Point3F> {
+  public static final Point3FParser INSTANCE = new Point3FParser();
+
+  private Point3FParser() {
+  }
+
+  @Override
+  public Point3F parse(JsonReader reader, float scale) throws IOException {
+    JsonReader.Token token = reader.peek();
+    if (token == JsonReader.Token.BEGIN_ARRAY) {
+      return JsonUtils.jsonToPoint3(reader, scale);
+    } else if (token == JsonReader.Token.BEGIN_OBJECT) {
+      return JsonUtils.jsonToPoint3(reader, scale);
+    } else if (token == JsonReader.Token.NUMBER) {
+      // This is the case where the static value for a property is an array of numbers.
+      // We begin the array to see if we have an array of keyframes but it's just an array
+      // of static numbers instead.
+      Point3F point = new Point3F((float) reader.nextDouble() * scale, (float) reader.nextDouble() * scale,
+          (float) reader.nextDouble() * scale);
+      while (reader.hasNext()) {
+        reader.skipValue();
+      }
+      return point;
+    } else {
+      throw new IllegalArgumentException("Cannot convert json to point. Next token is " + token);
+    }
+  }
+}