Sprites
Render image-based content from textures, sheets, SVG, and video.
Sprites
A Sprite is the primary drawable for textured quads — rectangles that display an image, a sub-region of an image, or a rendered-to-texture surface. Most visible content in a 2D scene passes through sprites.
From texture to screen
A sprite needs a texture. The shortest path from file to visible quad:
import { Scene, Sprite, Texture } from '@codexo/exojs';
class MyScene extends Scene {
async load(loader) {
await loader.load(Texture, { hero: 'image/hero.png' });
}
init(loader) {
this.hero = new Sprite(loader.get(Texture, 'hero'));
this.addChild(this.hero);
}
draw(context) {
context.backend.clear();
context.render(this.root);
}
}
The sprite’s rendered size defaults to the texture’s pixel dimensions. The sprite draws a quad from (0, 0) to (texture.width, texture.height) in local space.
Positioning and anchor
A sprite’s position places its anchor point in the parent’s coordinate space. By default the anchor is (0, 0) — the top-left corner — so setPosition(400, 300) places the top-left of the sprite at world position (400, 300).
setAnchor(0.5) centers the anchor at the sprite’s middle, making setPosition(400, 300) place the sprite’s center at (400, 300):
init(loader) {
const { width, height } = this.app.canvas;
this.hero = new Sprite(loader.get(Texture, 'hero'));
this.hero.setAnchor(0.5);
this.hero.setPosition(width / 2, height / 2);
this.addChild(this.hero);
}
setAnchor(x, y) accepts two arguments for separate horizontal and vertical anchors. A value of 0 is left/top, 0.5 is center, 1 is right/bottom.
Size and scale
sprite.width and sprite.height report the rendered pixel dimensions (texture frame size multiplied by absolute scale). Setting them adjusts the scale to match:
this.hero.width = 64; // scale.x becomes 64 / textureFrame.width
this.hero.height = 64; // scale.y becomes 64 / textureFrame.height
setScale(x, y) sets raw scale factors. A scale of (2, 2) doubles the sprite’s visual size; (-1, 1) mirrors it horizontally.
Tinting
sprite.tint is a Color that multiplies the sprite’s pixel colors at draw time. White tint (Color.white) leaves the texture unchanged. A red tint mutes green and blue channels. Tinting does not allocate or create new textures:
import { Color } from '@codexo/exojs';
this.hero.tint = new Color(255, 128, 128, 1); // reddish tint
this.hero.tint = new Color(128, 128, 255, 0.8); // blue tint, 80% opacity
Texture frames
A sprite can display a sub-region of its texture through textureFrame. This is the mechanism behind spritesheet frame selection:
import { Rectangle } from '@codexo/exojs';
// Show only the top-left 32x32 pixels of a 128x128 texture
this.sprite.setTextureFrame(new Rectangle(0, 0, 32, 32));
By default setTextureFrame resets the sprite’s width and height to the frame’s dimensions. Pass false as the second argument to keep the current display size — useful for animation where frame dimensions vary but the on-screen size should stay constant:
this.sprite.setTextureFrame(new Rectangle(32, 0, 32, 32), false);
Call resetTextureFrame() to restore the full texture.
Blend modes
sprite.blendMode controls how the sprite composites with pixels already in the framebuffer. The default is BlendModes.Normal (standard alpha blending):
import { BlendModes } from '@codexo/exojs';
this.glow.blendMode = BlendModes.Additive;
Blend modes are per-sprite. Combined with tinting they cover common glow, shadow, and knock-out compositing without needing custom shaders.
BlendModes implements the full W3C compositing/blending set. Normal, Additive, Subtract, Multiply, and Screen are fixed-function GPU blends with no extra draw cost. The remaining “advanced” modes go through a backdrop-aware compositor that copies the current render target before drawing each sprite, so they carry a small per-draw backdrop-copy cost — reach for them deliberately in draw-call-heavy scenes. isAdvancedBlendMode(mode) reports which category a given mode falls into.
| Mode | Visual effect |
|---|---|
Normal | Standard alpha compositing — source drawn over the backdrop. |
Additive | Adds source and backdrop colors; brightens. Good for glows, light, fire, particles. |
Subtract | Subtracts source from backdrop; darkens toward black. |
Multiply | Multiplies channel values; darkens, like stacking transparencies. |
Screen | Inverse of multiply; lightens, like projecting two images onto the same surface. |
Darken * | Keeps the darker of source and backdrop, per channel. |
Lighten * | Keeps the lighter of source and backdrop, per channel. |
Overlay * | Multiplies dark backdrop areas and screens light ones; boosts contrast. |
ColorDodge * | Brightens the backdrop to reflect the source color; intense highlights. |
ColorBurn * | Darkens the backdrop to reflect the source color; intense shadows. |
HardLight * | Overlay with source and backdrop roles swapped; harsh, directional lighting. |
SoftLight * | Softer version of Overlay; subtle, diffused lighting. |
Difference * | Absolute value of the channel difference; inversion / comparison effects. |
Exclusion * | Lower-contrast variant of Difference. |
Hue * | Source hue with backdrop saturation and luminosity. |
Saturation * | Source saturation with backdrop hue and luminosity. |
Color * | Source hue and saturation with backdrop luminosity; recolors while preserving shading. |
Luminosity * | Source luminosity with backdrop hue and saturation; inverse of Color. |
* Advanced mode — routed through the backdrop-aware compositor (isAdvancedBlendMode returns true).
Spritesheets
A Spritesheet slices a single texture atlas into named frames and optional animation sequences. It accepts a JSON descriptor in the Aseprite / TexturePacker format:
import { Spritesheet } from '@codexo/exojs';
const sheet = new Spritesheet(texture, {
frames: {
'walk_01': { frame: { x: 0, y: 0, w: 32, h: 32 } },
'walk_02': { frame: { x: 32, y: 0, w: 32, h: 32 } },
'walk_03': { frame: { x: 64, y: 0, w: 32, h: 32 } },
},
animations: {
walk: ['walk_01', 'walk_02', 'walk_03'],
},
});
// Pre-built Sprite for a single frame
const sprite = sheet.sprites.get('walk_01');
this.addChild(sprite);
// Rectangle for a single frame (useful for textureFrame)
const frame = sheet.frames.get('walk_01');
The animations map feeds into AnimatedSprite.fromSpritesheet(), covered in the Animation chapter.
Non-image textures
Sprite accepts any Texture or RenderTexture. This means sprites can display video frames, SVG rasterizations, off-screen renders, or DataTexture pixel buffers — any source the engine can wrap as a Texture:
// Video as a sprite texture
import { RenderTexture, Sprite, Video } from '@codexo/exojs';
const video = loader.get(Video, 'intro');
const sprite = new Sprite(video.texture);
// A RenderTexture (offscreen render) as a sprite texture
const rt = new RenderTexture(256, 256);
// ... render something into rt ...
const offscreenSprite = new Sprite(rt);
Sprite transforms in the graph
A Sprite inherits from Drawable, which extends RenderNode. This means every sprite carries a full transform (position, rotation, scale, origin) and participates in the scene graph as a node. When a sprite is a child of a rotated Container, it rotates with the container. When it has its own filters array, those filters apply to the sprite’s rendered quad before it composites into the parent.
Collision helpers — sprite.contains(x, y) for point tests and sprite.getBounds() for AABB queries — work on the sprite’s world-space quad and account for rotation.
Examples
A single sprite in the center of the canvas, rotating and cycling through tints.
Navigating a spritesheet’s frames by name — the texture-frame mechanism in practice.
Try it
Playground
API
Where to go next
The next chapter, Text, covers GPU-accelerated text rendering — font loading, styling, layout, and using text nodes in the scene graph.