Sprite

A textured 2D node: an image drawn in a Scene2D. Extends Node2D, so it has the full transform, hierarchy, events and aspect surface. To animate it from a sprite sheet, attach the SpriteAnimation aspect; to name an image's regions and clips in a file, use a sprite sheet (.sprite.ts).

  • draw an image in a 2D scene
  • show one frame or region of a texture atlas
  • put the sprite's pivot at its feet
  • resize a sprite or draw it at the image's pixel size
  • tint a sprite, fade it, or flip it to face left

Platforms: iOS, Android, desktop and the web player; not on the web (Web Lite); headless tests run it but draw nothing.

At a glance

TypeScript
async function main() {
  const scene = new Scene2D()
  const tex = await Texture2D.load(asset('./hero.png'))

  const hero = new Sprite({ texture: tex, anchor: [0.5, 1], layer: 1 })
    .aspect(SpriteAnimation, { size: [32, 48], fps: 10, clips: { idle: [0], walk: [1, 2, 3, 4] } })
  hero.position = [120, 64]
  hero.anim.play('walk')
  scene.add(hero)
  scene.open()
}
main()

Create a sprite: new Sprite

TypeScript
new Sprite(options?: {
  texture?: Texture2D | Canvas   // a Canvas bakes to a texture on assign
  anchor?: Vec2Like              // pivot: [0,0] top-left … [1,1] bottom-right; default [0.5, 0.5]
  size?: Vec2Like                // world size; default = the texture's pixel dimensions
  frame?: [u0, v0, u1, v1]       // normalized UV sub-rect
  color?: ColorInput             // tint, multiplied with the texture
  opacity?: number               // 0..1
  layer?: number                 // draw layer (see Scene2D layers)
  position?: Vec2Like
})
Note

anchor space is Y-down even though the world is Y-up: [0.5, 1] is the sprite's bottom-center ("feet"). Use it for anything on a Y-sorted layer so depth sorts by feet.

Texture and size: texture, size

TypeScript
sprite.texture = tex             // Texture2D, or a Canvas (baked on assign)
sprite.size = [w, h]             // world units; an explicit size survives a texture change
  • Default size is the texture's pixel dimensions — 1 texel = 1 world unit = 1 logical px at zoom 1.
  • A Canvas-backed sprite defaults to the canvas's logical size, so pixelRatio on the canvas affects crispness only, never on-screen size. See Canvas.
  • Assigning a texture resets the frame to the whole texture.
  • A sprite without a texture draws nothing.

Frames (atlases / spritesheets)

TypeScript
sprite.frame = [u0, v0, u1, v1]        // normalized UV sub-rect; [0, 0] = the texture's top-left
sprite.setFramePx(x, y, w, h): this    // sub-rect in texture pixels — chainable

setFramePx is the one to use with an atlas: coordinates match what an image editor shows. Changing the frame does not change size — set size once to the frame's pixel dimensions if you want unscaled output.

TypeScript
async function main() {
  const scene = new Scene2D()
  const tex = await Texture2D.load(asset('./items.png'))

  const coin = new Sprite({ texture: tex, size: [16, 16], position: [40, 40] })
    .setFramePx(32, 0, 16, 16)   // the cell at x 32, y 0 of the atlas, drawn unscaled
  scene.add(coin)
  scene.open()
}
main()

For frame-by-frame animation, don't drive frame yourself — attach SpriteAnimation and define clips.

Tint, fade and flip: color, opacity, flipX

TypeScript
sprite.color = '#ff8800'    // tint (multiplied); default white = untinted
sprite.opacity = 0.5        // 0..1
sprite.flipX = true         // mirror horizontally (facing left/right without negative scale)
sprite.flipY = true
sprite.animateTo({ opacity: 0, color: '#ff0000', duration: 300 })   // + the Node2D transform props

The tint's alpha is ignored — fade with opacity. animateTo / animateFrom take opacity and color on top of the transform props and return the Animation handle.

Pitfalls

TypeScript
// ✗ expecting scale to resize a declared collider — explicit Shape2D extents ignore scale
sprite.scale = 2   // draws 2×, but its physics box stays the size you declared

// ✗ animating by swapping frames yourself — works, but re-implements SpriteAnimation
sprite.setFramePx(frame * 32, 0, 32, 48)   // from a per-frame update

See also