Skip to content
Back to skills

Scene 3d Authoring

ASecurity

Build a 3D scene (a composition whose space is 3d) — models, camera, lighting presets, floor, groups and parenting, model parameters, 3D motion presets (drop_in, fly_in_arc, snap_settle, bounce, idle_bob, wiggle, turntable; camera orbit, dolly, push_in, crane, whip_pan) — then render and measure it. Use when making a product or toy showcase, a build-up sequence of parts, an object that builds itself and then moves (drives, walks, unfolds), a turntable, a 3D title or any 3D document, or when 3...

  • 6 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added November 5, 2026
ai-agentsgonodeexpressaws

Works with

  • cli

Security analysis

A100/100

Pro scans all 3 files and shows the line behind each finding

Scanned November 5, 2026

npx -y skills add PicsArt/gen-ai-skills --skill scene-3d-authoring --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Scene 3d Authoring?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Scene 3d Authoring
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/picsart-scene-3d-authoring/badge)](https://www.skillsdirectory.com/skills/picsart-scene-3d-authoring)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: scene-3d-authoring
description: Build a 3D scene (a composition whose space is 3d) — models, camera, lighting presets, floor, groups and parenting, model parameters, 3D motion presets (drop_in, fly_in_arc, snap_settle, bounce, idle_bob, wiggle, turntable; camera orbit, dolly, push_in, crane, whip_pan) — then render and measure it. Use when making a product or toy showcase, a build-up sequence of parts, an object that builds itself and then moves (drives, walks, unfolds), a turntable, a 3D title or any 3D document, or when 3D motion lands wrong, sinks into the floor or fails to evaluate. Not for a 2D cartoon character, face or mascot (shapes and the cartoon rig templates, in character-rigging), nor for flat layers in depth (photo cards that flip, a camera orbiting cards or panels) — that is a 2D scene with layer depth and the composition camera, in scene-animation-reference.
metadata:
  category: authoring
  version: '1.1'
  ported-from: 3d-authoring 1.1 at 55.91.0
user-invocable: false
---

# 3D scenes — models, light, motion, render

A 3D scene is a scene whose composition says `space: "3d"`: metres, y up, rotations in
degrees about x then y then z (intrinsic). Its layers are objects (a `model`, a `media` or 2D-scene card,
a `scene_ref` group, or a `light`), it is seen through one camera, and it plays as a rendered video:
`picsart_scene_contact_sheet` and `picsart_scene_export` render it, whether the 3D scene is the document
itself or one a 2D scene plays through a `scene_ref`. **Ask `picsart_scene_capabilities` for
`schema:Composition3D` and `schema:Layer3D` before writing a field** — a 3D object refuses 2D placement
fields (`anchor`, `effects`, `looks`, `transforms`) by name.

Flat layers in depth are not a 3D scene. Photo cards that flip in, panels that fan out, a camera
that orbits them: keep the 2D scene and give its layers `z`/`rotationX`/`rotationY` under the
composition camera (`picsart_scene_recipe scene-animation-reference`, "Depth and the camera"). The catalog
entries that work that way are tagged `2.5d` (`flip_in`, `flip`, `tilt_3d`, and the camera moves
`orbit`, `dolly`, `push_in`).

Every asset a 3D scene names — a model, a picture, a video — is a public https URL. Build the
document inline and change it with `picsart_scene_patch`.

## Start from a frame, a camera and a light

```json
{ "version": "1.0",
  "composition": { "space": "3d", "width": 1920, "height": 1080, "fps": 30, "duration": 8,
    "camera": { "eye": [0, 0.75, 1.7], "target": [0, 0.08, -0.1], "shutterAngle": 180 },
    "motionBlurSampleCount": 8 },
  "assets": { "brick": { "type": "model", "uri": "https://cdn.example.com/brick.glb" } },
  "layers": [ { "id": "b1", "content": { "kind": "model", "assetId": "brick",
      "materials": { "*": { "baseColor": "#e3000b", "roughness": 0.35, "clearcoat": 0.6 } } },
    "transform": { "position": [0, 0, 0], "scale": [10, 10, 10] } } ] }
```

- **Camera.** `eye` is where it stands, `target` what it looks at, both in metres; `lens.fov`
  is vertical degrees (default 35). Frame the subject by moving the eye, not by scaling the
  subject. `shutterAngle` 180 with 8 samples is film motion blur for the whole frame.
- **Light.** Set `composition.lighting` (`schema:Composition3D`): a `preset` — `studio`, `dark`,
  `sky` or `white_room` — then tune any one light over it (`key`, `fill`, `rim`, `ambient`: a key's
  `intensity`, `color`, `direction`, `shadow.softness`). `background`, `environment` and `ground`
  finish the look: a warm paper sweep with a soft-shadowed key is `studio`; a low orange key from the
  side with a dusk-gradient `background` reads as sunset; near-black with a bright cool `rim` is dramatic.

- **Post effects.** `composition.effects` is the 3D scene's post stack: `glow`,
  `ambient_occlusion`, `depth_of_field`, `color_grade`, `vignette`, `film_grain`,
  `channel_shift`. Their params, ranges and units are the `space: "3d"` entries of
  `picsart_scene_capabilities` `effects` — `sections: ["effect:ambient_occlusion"]` for one, or
  the whole `effects` section, keeping the entries whose `space` is `3d`. A glow spreads only emitted light: a lamp or an LED
  (`emissive` with an `emissiveStrength` above the `threshold`), never a lit surface.
- **Floor.** `ground` is the plane y = 0 and takes the key's shadows. An object stands on it
  when its lowest point is at y = 0 — see *Models* for where that is. Over a drawn floor the
  backdrop shows only where the floor does not: a camera looking down sees floor, not sky. To
  show a gradient or sky, lower the eye or raise the target until the horizon is in frame.
- **Seamless sweep.** A lit floor never matches an unlit backdrop: the lights shade it, so its
  horizon shows. `ground.shadowOnly: true` over a `background` draws the backdrop itself as the
  floor, with only the shadows on it (`studio` does this), and no horizon at any camera height.
- **Matte.** `ground.shadowOnly: true` with no `background` renders a matte that carries the
  shadows onto whatever 2D scene plays it.
- **Casts nothing.** A sky dome or a far backdrop model takes `content.castShadow: false`: it neither shuts
  the key out nor widens the shadow's fit, which follows the casters only, so shadows stay sharp.

## Models

A model is a GLB `model` asset at a public https URL. Asset keys are identifiers: `p3004`, never `3004`.

- **Origin at the base.** The origin is what `position` places, what a turn pivots on and
  what a squash flattens toward. With the origin at the centre of the base the object stands on
  the floor at `[x, 0, z]` and squashes onto it. A model whose origin is elsewhere is lifted by its
  height (the import report gives its bounds), or fixed in the file it came from.
- **Bring a model in.** `picsart_scene_import_model` takes a GLB, a glTF (buffers and images
  embedded as data: URIs) or a binary FBX from a public https URL, Draco or meshopt compressed
  included, and returns one uncompressed GLB plus a report of its nodes, bones, morph targets,
  materials and clips, with the model parameters it proposes. Reference the `url` it returns (the
  Drive copy when `driveUid` comes back; an `expiring` one must be re-imported before a scene
  depends on it). `expose` picks the parameter kinds (`bones`, `morphs`, `materials`, `clips`);
  a bone a clip already turns is proposed only when `bones` is named, so a walk clip still walks.
  Look at it with `picsart_scene_preview_model` (a still or a turntable) before building on it.
- **A ready-made model.** `picsart_scene_build_model` builds one GLB: `character` (a rigged toy
  bear with `blink`, `aa`, `happy` and body controls such as `headNod`, `leftUpperArmRaise`) or
  `set` (a meadow of props: path, hills, trees, flowers, blocks, mushrooms, castle, clouds, fence,
  bunting, balloons, sky; the same arguments build the same model). `sky` is an unlit dome: give that
  layer `castShadow: false`. Build the far set (sky, hills, clouds) and the near one (trees, fence,
  flowers) as two models.
- **Materials.** `content.materials` overrides by material name, `"*"` for all of them.
- **Model parameters.** A knob the model declares (a colour that makes the studs glow, a bone,
  a shape key) is set per layer through `content.parameters` — a constant or a track, so it can flash
  on a beat. What a parameter binds, aggregate mode, response curves, several actions mixed in tiers,
  lip sync, the standard character control names and a car without bones:
  [references/model-parameters.md](references/model-parameters.md).
- **A face or screen on a model** is a material texture slot, not a card:
  [references/model-parameters.md](references/model-parameters.md), "Faces and screens on a model".

## Groups and parenting

- **Group** — several objects that move as one: a `scene_ref` layer whose scene is a 3D
  scene with only `space` (and `duration`) in its composition. The group's `transform` and
  `animations` place the whole; its objects keep their own motion inside, on the group's clock.
  A sub-assembly built beside the main model, then flown in, is a group.
- **`parentId`** — one object rides a sibling's placement (position, rotation, scale) at
  every instant, keyframes and expressions included; opacity and time stay its own. Use it for
  a part that must stay attached to something that moves.
- **A carrier** — motion with nothing drawn, for several objects to ride: a group whose scene
  has no layers, holding the motion, and the objects parented to it. An object's own
  `animations` then stay free for its entrance or its loop.

  ```json
  "assets": { "rig": { "type": "scene", "scene": { "composition": { "space": "3d" }, "layers": [] } } },
  "layers": [
    { "id": "rig", "content": { "kind": "scene_ref", "assetId": "rig" },
      "animations": { "position": { "keyframes": [{ "time": 0, "value": [-1, 0, 0] }, { "time": 2, "value": [1, 0, 0] }] } } },
    { "id": "car", "parentId": "rig", "content": { "kind": "model", "assetId": "car" } }
  ]
  ```

## Lights

- **A light layer** — `content: { kind: "light", type: "point" | "spot", intensity, color }`: a light that
  stands in the scene, placed by the layer's `transform` and `animations` and parented by `parentId`. The rig's
  `key`, `fill` and `rim` are directional and light everything; a light layer lights what is near it. `scale`
  and `opacity` on it are refused (`light_layer_field`); dim it with `intensity`.
- **Units.** At 1 m a surface facing it is as bright as under a key of the same `intensity`; it falls off as
  `1 / d^decay` (`decay` 2 by default), and `range` (metres) fades it to nothing there.
- **A spot** shines along the layer's −z: `rotation: [-90, 0, 0]` shines straight down. `angle` is degrees from
  its axis to the cone's edge (default 30), `penumbra` 0..1 softens the edge.
- **Shadows.** `shadow: { softness }` makes it cast them (a point light every way; a spot's `angle` at most 85);
  omitted, it casts none. Each one renders a shadow map (six for a point light) every frame: give it to the
  lights that need one.
- **Its tracks** are on the layer's clock. A colour expression returns `Color.rgb(r, g, b)` (0..1), never a hex
  string. Lights pulsing on a beat: [references/recipes.md](references/recipes.md).

## Cards

A card is a flat picture `width` metres wide (default 1), its height from the picture's aspect,
facing +z; `lit: false` shows it at its own colours.

- **Media card** — `content.kind: "media"` with an `image`, `svg`, `video` or `video_with_matte`
  asset. A video plays on the layer's clock: at layer time t it shows `trim.in + t × speed`,
  holding its last frame past `trim.out` unless `loop`. A `video_with_matte` is cut out by its
  matte. A card plays no sound: put the audio in the 2D scene around the 3D one.
- **Scene card** — a `scene_ref` whose scene is 2D (`assetId`, or an inline `asset`): the scene
  drawn flat, its composition `width`/`height` its aspect. `parameters` are its bindings, keyed
  on the card layer's clock: a sign's text, a screen's colour, a track a 2D template animates.
  A card's body is written out: to play a template, the body declares the parameters it takes
  and passes them to one `scene_ref` layer pointing at the template. `width` and `lit` belong to
  a scene card only; a group refuses them.
- A card is a flat plane: use it for screens, signs, posters and other 2D in 3D. It does not
  follow a bone; a picture painted on a model's surface is a material slot.

```json
{ "id": "sign", "transform": { "position": [0, 1.6, -2] },
  "content": { "kind": "scene_ref", "width": 1.2, "lit": true,
    "asset": { "type": "scene", "scene": { "composition": { "width": 600, "height": 200, "duration": 4 },
      "parameters": { "label": { "type": "text", "default": "OPEN" } },
      "layers": [{ "id": "t", "content": { "kind": "text", "text": { "$eval": "label" } } }] } },
    "parameters": { "label": "PLAY TIME" } } }
```

## Motion presets

`picsart_scene_apply_preset` with `preset: "motionPreset:<id>"` puts a preset on a 3D object
(`layerId`) or on the camera (no `layerId`). Defaults are tuned; pass only what you change. `orbit`,
`dolly` and `push_in` also name a 2D composition-camera preset; on a 3D scene the 3D one below
applies. Every `motionPresets` entry of `picsart_scene_capabilities` says its `space`; keep the
entries whose `space` is `3d`.

| Object preset | What it does | Params (default) |
|---|---|---|
| `drop_in` | Falls from above, lands, squashes | `duration` 0.5, `height` 0.6, `squash` 0.12 |
| `fly_in_arc` | Flies in on a rising arc, spinning, slows into place | `duration` 0.8, `distance` 0.8, `direction` right, `arc` 0.4, `spin` 1 |
| `snap_settle` | Drawn in, overshoots, swings back, squashes on contact | `duration` 0.3, `distance` 0.15, `direction` down, `intensity` 0.3, `settle` 10, `bounces` 25, `squash` 0.1 |
| `bounce` | Hops and lands, squashing, every period | `height` 0.15, `period` 0.6, `squash` 0.1 |
| `idle_bob` | Slow rise, fall and tilt that never resolves | `period` 3, `distance` 0.03, `angle` 2 |
| `wiggle` | Smooth never-repeating drift and turn | `frequency` 1.5, `amount` 0.02, `angle` 3, `seed` 0 |
| `turntable` | Turns about the world vertical, keeping its tilt | `period` 8, `direction` counterclockwise |

| Camera preset | What it does | Params (default) |
|---|---|---|
| `orbit` | Circles the point it looks at | `start` 0, `duration` 6, `angle` 360, `easing` ease_in_out |
| `dolly` | Travels along the view, target with it | `start` 0, `duration` 2, `distance` 1 |
| `push_in` | Closes in on the target, which stays | `start` 0, `duration` 3, `amount` 0.3 |
| `crane` | Rises, still looking at the target | `start` 0, `duration` 2.5, `height` 1 |
| `whip_pan` | Swings the view fast, standing still | `start` 0, `duration` 0.3, `angle` 70, `direction` left |

How they combine:

- **An object's motion is relative to where it rests.** Entrances end exactly at the object's
  `transform`, loops start from it. Move the object afterwards and its motion moves with it.
  Time is the object's own: an entrance starts at the object's `start`.
- **One channel carries one expression.** Two presets that both drive an object's position
  (say `drop_in`, then `idle_bob`) — the second replaces the first, and the result says so in
  `notes`. To follow an entrance with a loop, give the loop its own object: a group or a
  `parentId` parent holding the loop, the object holding the entrance.
- **Camera moves chain.** Each move starts where the camera is at its `start`; keyframed moves
  (`dolly`, `push_in`, `crane`) append, so `dolly` 0–2 then `crane` 2–4 runs as one path. A move
  starting inside existing camera keyframes is refused; start it at or after the last one.
  `orbit` and `whip_pan` ride whatever path is there and hold their end angle — so apply them
  **last**: a keyframed move after one cannot tell where the camera is, and is refused. They
  turn about the point the camera looks at (orbit) or stands at (whip pan) as it is at their
  `start`, so that point must have stopped moving by then. Each reads the channel the other
  turns, so a camera takes one of them: orbit through a shot, or whip pan out of one.

Worked sequences — a build of parts on a beat grid, an object that builds itself and then drives
off, the whole scene pulsing on the beat — are in [references/recipes.md](references/recipes.md).

## Render and verify

- **Validate.** `picsart_scene_validate`; a 3D document always carries a warning that a translated
  target cannot draw it — that warning is expected, nothing else is. Validation does not read a model
  at a URL: a `materials` override or `action` the model lacks shows up only when it renders, so take
  the names from the `picsart_scene_import_model` report.
- **Render.** `picsart_scene_contact_sheet` with `times` draws the moments you name into one sheet;
  `picsart_scene_export` renders the video, or one frame with `mediaType: "png"` and `startTime`. Both
  take the 3D document as it is, or a 2D scene that plays it, and draw the same frame at the same
  time. `parameters` set the scene's declared parameters on both. Look at **one** contact sheet
  through several `times`, not one render per still.

  ```jsonc
  // a bare 3D scene at fps 15 declaring `tilt`; times are multiples of 1/15 (the render's frame grid):
  picsart_scene_contact_sheet { "scene": <scene>, "parameters": { "tilt": 30 }, "times": [0.4, 1.2, 1.8] }
  ```

- **Text goes over it in 2D.** A 3D object is a model, a card or a group — no text drawn in 3D. A
  title is a 2D scene playing the 3D one through a `scene_ref` whose `parameters` pass the caller's
  values on, with the text layer beside it; render and export that 2D scene as it is.
- **Measure, don't eyeball.** Render a check view: a side or top `camera`, no `shutterAngle`, a
  small `width`/`height`, and a contact sheet with `times` around the motion — exact frames to
  look at. `picsart_media_track_motion` on the exported video (`targets: [{ bbox }]` on the moving
  part) returns the part's position every frame — of the final composite, so a 2D layer drawn over
  the part, or a background of the part's colour, moves the box too: track a check view with a plain
  contrasting `background` on the 3D document and nothing over it. Check that an entrance lands
  exactly on its `transform`, that nothing sinks below y = 0, that a moving part's box never crosses
  one already placed, and that the camera path is continuous across shot seams.
- **Pitfalls.**
  - A `parameters` name the scene does not declare is refused, naming the tool and the names it
    does declare: read them from the scene's `parameters` block, not from memory.
  - The render is a video at the scene's `fps`: a time between two of its frames draws one of them.
    Ask for times on the grid (`n / fps`) when comparing moments, or two "different" times can show
    the same frame.

## Gotchas

- **A one-number `scale` is one number to an expression.** `transform.scale: 10` makes a
  hand-written expression's `value.y` fail; write `[10, 10, 10]`. Presets convert it for you.
- **An expression returns `Vec.v3(x, y, z)`, never a Lua table** — `{1, 2, 3}` fails to
  evaluate. Read components as `value.x` or `value[1]`; `value * 2` and `value + Vec.v3(…)` work.
- **Another layer's depth is `getPlacement():z()`**, beside `position()` and its heading `rotationY()`.
- **The camera's `eye` and `target` have no resting value**, so an expression on one needs
  keyframes to start from; `orbit` and `whip_pan` write that keyframe for you.
- **Rotation keyframes interpolate the numbers.** `[0, 0, 0]` to `[0, 360, 0]` is a full turn;
  `[0, 350, 0]` to `[0, 10, 0]` turns 340° back. A turn about the world vertical on a tilted
  object is `turntable`, not a y keyframe.
- **Final values exactly.** A landing pose written with rounded numbers lands a hair off its
  neighbours; write the same numbers as the resting `transform`.
- **Something sinks into the floor.** Its origin is not at its base — lift it by its height.
- **A squash lifts the object off the floor.** Same cause: a squash flattens toward the origin.
- **The backdrop never shows.** The camera is looking down at a drawn floor; bring the horizon
  into frame, or make the ground `shadowOnly`.

Files in this skill

  • SKILL.md18.9 KB
  • references/model-parameters.md9.5 KB
  • references/recipes.md6.2 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

View all in ai-agents →

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.