Actor
An Actor is any renderable entity you place in a scene. Every actor shares a common set of transform and event props, but the type field determines what gets rendered:
| Type | What it renders |
|---|---|
"model" | A 3D model loaded from a .glb / .gltf file |
"sprite" | A flat 2D image (static or animated spritesheet) |
"primitive" | A code-generated geometric shape (box, sphere, etc.) |
import { Actor } from "@carverjs/core/components";Choosing an Actor Type #
Not sure which type to use? Here's a guide.
Model (type: "model") #
Loads a pre-made 3D model file (.glb / .gltf) created in tools like Blender, Maya, or downloaded from asset stores.
Use for: Complex, detailed 3D objects — characters, buildings, vehicles, weapons, furniture. Anything that has detailed geometry, textures, and potentially skeletal animations baked into the file.
<Actor type="model" src="/character.glb" animationName="idle" />Full 3D with complex geometry and textures from the file
Supports built-in skeletal animations (walk, idle, attack cycles)
Requires an external
.glb/.gltfasset fileHeaviest option (file download + GPU resources)
Sprite (type: "sprite") #
A flat 2D image (.png, .jpg) rendered in the 3D/2D world. Sprites come in three flavours:
Static — a single image displayed as-is
Grid-based animation — a spritesheet image where all frames are arranged in a grid (no extra files needed)
Atlas-based animation — a spritesheet image paired with a JSON atlas that describes each frame's position and size
Use for: 2D games, UI elements in 3D space, particle-like effects, trees/foliage in retro-style games, health bars, damage numbers. Think classic 2D game characters or "billboard" elements that always face the camera.
// Static image
<Actor type="sprite" src="/tree.png" />
// Grid-based animated spritesheet (image only, no JSON needed)
<Actor
type="sprite"
src="/character-run.png"
spriteAnimation={{ numberOfFrames: 10, fps: 8, loop: true }}
/>
// Atlas-based animated spritesheet (image + JSON atlas)
<Actor
type="sprite"
src="/player.png"
atlasUrl="/player.json"
spriteAnimation={{ fps: 12, loop: true }}
/>Flat 2D image, no 3D depth
Optionally billboards (always faces camera) — great for 2D characters in 3D worlds
Two animation methods: grid-based (just an image) or atlas-based (image + JSON)
Lighter than models, requires image assets
Primitive (type: "primitive") #
A basic geometric shape (box, sphere, cylinder, etc.) generated entirely by code — no external files needed.
Use for: Prototyping, debug visuals, simple game objects (platforms, walls, collectible orbs, ground planes), placeholder art during development, or minimalist/abstract game styles.
<Actor type="primitive" shape="sphere" color="red" />
<Actor type="primitive" shape="box" wireframe color="lime" />No asset files needed — pure code
9 shapes:
box,sphere,cylinder,cone,torus,plane,circle,capsule,ring5 material types with color, wireframe, and opacity control
Lightest option, instant rendering
No animation support (geometry only)
At a Glance #
| Model | Sprite | Primitive | |
|---|---|---|---|
| Dimensions | Full 3D | 2D flat | 3D geometric |
| Assets needed | .glb/.gltf file | .png/.jpg image | None |
| Animation | Skeletal (walk, idle...) | Spritesheet frames (grid or atlas) | None |
| Complexity | High (detailed meshes) | Low (flat image) | Minimal (basic shape) |
| Best for | Characters, props, buildings | 2D games, billboards, UI | Prototyping, platforms, debug |
Quick Start #
A few copy-paste examples to get you going.
Primitive — a red box #
<Actor type="primitive" shape="box" color="#ff0000" position={[0, 1, 0]} castShadow />Model — a character with an animation #
<Actor type="model" src="/models/character.glb" animationName="Idle" position={[0, 0, 0]} />Sprite — a billboard tree #
<Actor type="sprite" src="/textures/tree.png" position={[3, 1, 0]} billboard />Sprite — grid-based animated spritesheet #
If your spritesheet is a single image with frames arranged in a grid, just pass spriteAnimation with a numberOfFrames count. CarverJS automatically calculates the rows and columns from the image dimensions.
<Actor
type="sprite"
src="/sprites/hero-run.png"
spriteAnimation={{ numberOfFrames: 10, fps: 8, loop: true }}
/>Sprite — atlas-based animated spritesheet #
If you have a JSON atlas file (exported from tools like TexturePacker or Aseprite), pass it via atlasUrl alongside the spritesheet image.
<Actor
type="sprite"
src="/sprites/hero.png"
atlasUrl="/sprites/hero.json"
spriteAnimation={{ fps: 12, loop: true, autoPlay: true }}
/>Props Reference #
Actor is a discriminated union on the type field. All three variants share a common base of transform, visibility, and event props.
Common Props #
These apply to every actor regardless of type.
| Prop | Type | Default | Description |
|---|---|---|---|
type | "model" | "sprite" | "primitive" | — | Required. Determines the renderer used |
name | string | — | Name assigned to the group |
position | Vector3 | — | World position [x, y, z] |
rotation | Euler | — | Rotation [x, y, z] |
scale | Vector3 | — | Scale [x, y, z] or uniform number. Overrides size if both are set |
size | number | — | Uniform scale factor — preserves aspect ratio. size={2} doubles the actor in all axes |
visible | boolean | true | Toggle visibility |
castShadow | boolean | false | Whether meshes cast shadows |
receiveShadow | boolean | false | Whether meshes receive shadows |
renderOrder | number | — | Render order override |
physics | ActorPhysicsProps | — | Rapier physics body config. Only active when parent World has physics. |
userData | Record<string, unknown> | — | Arbitrary user data on the group |
children | ReactNode | — | Additional children inside the group |
Event Props #
All R3F pointer, click, and wheel events are supported:
onClick, onDoubleClick, onContextMenu, onPointerUp, onPointerDown, onPointerOver, onPointerOut, onPointerEnter, onPointerLeave, onPointerMove, onPointerMissed, onWheel
Model Props #
Additional props when type is "model".
| Prop | Type | Default | Description |
|---|---|---|---|
src | string | — | Required. URL to a .glb/.gltf file |
useDraco | boolean | string | — | Enable Draco decompression (pass true or a decoder path) |
useMeshopt | boolean | — | Enable Meshopt decompression |
animationName | string | — | Name of the animation clip to play |
animationPaused | boolean | false | Pause the active animation |
animationSpeed | number | 1 | Playback speed multiplier |
animationLoop | boolean | true | Whether the animation loops |
Sprite Props #
Additional props when type is "sprite".
| Prop | Type | Default | Description |
|---|---|---|---|
src | string | — | Required. URL to the texture image |
atlasUrl | string | — | URL to a spritesheet JSON atlas (for atlas-based animation) |
billboard | boolean | true | Auto-face the camera |
billboardLockX | boolean | — | Lock billboard rotation on X axis |
billboardLockY | boolean | — | Lock billboard rotation on Y axis |
billboardLockZ | boolean | — | Lock billboard rotation on Z axis |
spriteAnimation | object | — | Animation settings (see below). When provided without atlasUrl, enables grid-based animation. When provided with atlasUrl, enables atlas-based animation |
How Sprites Render #
The combination of atlasUrl and spriteAnimation determines what gets rendered:
atlasUrl | spriteAnimation | Result |
|---|---|---|
| — | — | Static sprite — a single image |
| — | Provided | Grid-based animation — frames are auto-detected from the image grid |
| Provided | Provided | Atlas-based animation — frames are read from the JSON atlas |
spriteAnimation Fields #
| Field | Type | Default | Grid | Atlas | Description |
|---|---|---|---|---|---|
numberOfFrames | number | 1 | Required | Optional | Total number of frames in the spritesheet |
fps | number | 10 | Yes | Yes | Frames per second |
loop | boolean | true | Yes | Yes | Loop the animation |
startFrame | number | 0 | Yes | Yes | First frame index |
endFrame | number | last frame | Yes | Yes | Last frame index |
autoPlay | boolean | true | Yes | Yes | Start playing immediately |
flipX | boolean | false | Yes | Yes | Mirror horizontally |
frameName | string | — | — | Yes | Named frame to display |
play | boolean | — | — | Yes | Start playback |
pause | boolean | — | — | Yes | Pause playback |
alphaTest | number | — | — | Yes | Alpha threshold for transparency |
playBackwards | boolean | — | — | Yes | Reverse playback direction |
resetOnEnd | boolean | — | — | Yes | Reset to first frame when done |
animationNames | string[] | — | — | Yes | Available animation names in the atlas |
onStart | function | — | — | Yes | Callback when animation starts |
onEnd | function | — | — | Yes | Callback when animation ends |
onLoopEnd | function | — | — | Yes | Callback when a loop cycle ends |
onFrame | function | — | — | Yes | Callback on each frame change |
Grid-based animation is the simplest way to animate a sprite — just provide a single spritesheet image and set numberOfFrames. CarverJS automatically calculates the grid layout (rows and columns) from the image dimensions. No JSON atlas file or external tools required.
Primitive Props #
Additional props when type is "primitive".
| Prop | Type | Default | Description |
|---|---|---|---|
shape | PrimitiveShape | "box" | Geometry type |
geometryArgs | number[] | per-shape defaults | Constructor arguments for the geometry |
materialType | PrimitiveMaterialType | "standard" | Material type |
color | ColorRepresentation | "#6366f1" | Material color |
materialProps | Partial<MeshStandardMaterial> | — | Additional material properties |
wireframe | boolean | — | Render as wireframe |
Available Shapes #
| Shape | Default geometryArgs |
|---|---|
box | [1, 1, 1] |
sphere | [0.5, 32, 32] |
cylinder | [0.5, 0.5, 1, 32] |
cone | [0.5, 1, 32] |
torus | [0.5, 0.2, 16, 32] |
plane | [1, 1] |
circle | [0.5, 32] |
capsule | [0.5, 1, 4, 16] |
ring | [0.3, 0.5, 32] |
Available Material Types #
| Type | Description |
|---|---|
"standard" | Physically-based material (default) — responds to lighting realistically |
"basic" | Flat shading, not affected by lights — good for UI or unlit objects |
"phong" | Shiny specular highlights — good for glossy surfaces |
"lambert" | Soft diffuse shading — good for matte surfaces |
"toon" | Cel-shaded / cartoon look — hard-edged lighting bands |
Physics Props #
When the parent World has a physics prop, you can add physics to individual Actors via the physics prop. The Actor is wrapped in a Rapier <RigidBody>.
| Field | Type | Default | Description |
|---|---|---|---|
bodyType | RigidBodyType | "dynamic" | "dynamic", "fixed", "kinematicPosition", or "kinematicVelocity" |
collider | PhysicsColliderType | "auto" | "cuboid", "ball", "capsule", "trimesh", "convexHull", or "auto" |
mass | number | 1 | Mass of the body |
restitution | number | 0 | Bounciness (0-1) |
friction | number | 0.5 | Friction coefficient |
sensor | boolean | false | Trigger volume (detects overlap, no physics response) |
gravityScale | number | 1 | Per-body gravity multiplier. 0 = no gravity |
linearDamping | number | 0 | Linear velocity damping |
angularDamping | number | 0 | Angular velocity damping |
ccd | boolean | false | Continuous collision detection for fast-moving objects |
enabledTranslations | [boolean, boolean, boolean] | — | Lock/unlock translation per axis. In 2D, Z is auto-locked |
enabledRotations | [boolean, boolean, boolean] | — | Lock/unlock rotation per axis. In 2D, X/Y are auto-locked |
onCollisionEnter | CollisionCallback | — | Called when another body starts touching |
onCollisionExit | CollisionCallback | — | Called when another body stops touching |
<Actor
type="primitive" shape="box" color="red" position={[0, 5, 0]}
physics={{ bodyType: "dynamic", mass: 2, restitution: 0.5 }}
/>Ref #
Actor forwards a ref to its root <group>, giving you direct access to the Three.js object for imperative control:
const actorRef = useRef<Group>(null);
<Actor ref={actorRef} type="primitive" shape="sphere" />Type Definitions #
See Types for all type definitions including ActorProps, ModelActorProps, SpriteActorProps, PrimitiveActorProps, ActorTransformProps, ActorEventProps, ActorPhysicsProps, PrimitiveShape, and PrimitiveMaterialType.