Image
import { Image, Spritesheet, Animation } from "lumen:image"
The image module holds the classes a game draws with — Image, Spritesheet, and Animation. It has no free functions of its own: everything here is a class you new, and drawing happens through the object rather than through the module.
Paths resolve relative to the directory your game's entry file is in.
Classes
new Image(path)
Loads an image from a path.
import { Image } from "lumen:image"
function load() {
game.player = new Image('resources/player.png')
}
Loading the same path twice hands back the image already in memory rather than uploading a second copy of it, so loading a shared sheet from two places costs one texture.
Load in load(), not in draw(). PNG, JPEG, and the other formats SDL2_image supports all work.
new Spritesheet(path, frameSize)
Cuts an image into a grid of equally sized frames.
import { Spritesheet } from "lumen:image"
sheet = new Spritesheet('characters.png', 16) // square frames
sheet = new Spritesheet('characters.png', 16, 24) // 16 wide, 24 tall
The first argument may also be an image that is already loaded, which is how two sheets get cut from one file at different frame sizes.
new Animation(sheet, frames, secondsPerFrame, [mode])
Plays a sequence of a sheet's frames against a clock. See Animation.
import { Animation } from "lumen:image"
walk = new Animation(sheet, [4, 5, 6, 7], 0.16)
Quad lives on canvas, not here — import { Quad } from "lumen:canvas".Image
Built with new Image(path).
| Method | What it does |
|---|---|
draw(x, y, [rotation, sx, sy, ox, oy]) | Draws the image. |
drawQuad(quad, x, y, [rotation, sx, sy, ox, oy]) | Draws one region of it. |
clip(x, y, size) or clip(x, y, w, h) | Returns a lightweight view onto part of the image. |
getWidth() / getHeight() | Dimensions in pixels. |
getDimensions() | Both, as [width, height]. |
getPixel(x, y) | The color of one pixel of the source image. |
setFilter('nearest'|'linear') | How the image is sampled when scaled. |
Rotation is in radians, applied about the origin offset (ox, oy). Negative scale flips:
sprite.draw(x, y) // plain
sprite.draw(x, y, 0, 2, 2) // double size
sprite.draw(x, y, 0, -1, 1) // mirrored horizontally
sprite.draw(x, y, angle, 1, 1, 8, 8) // rotated about its own centre, for a 16x16 sprite
clip() is how one spritesheet becomes many sprites, and it reuses the view it made for a region last time, so reaching for the same region every frame allocates nothing.
getPixel() reads from the source image rather than the screen, which lets collision or spawn data be baked into a map image.
setFilter('nearest') is what pixel art wants — 'linear' blurs it.
Spritesheet
One image cut into a grid of equally sized frames, numbered left to right and top to bottom from zero.
| Method | What it does |
|---|---|
draw(frame, x, y, [rotation, sx, sy, ox, oy]) | Draws one frame. |
getQuad(frame) | The Quad for a frame. |
getImage() | The underlying Image. |
getCount() | How many frames the sheet holds. |
getColumns() / getRows() | The grid's shape. |
getFrameWidth() / getFrameHeight() | Frame size in pixels. |
getFrameDimensions() | Both, as [width, height]. |
sheet = new Spritesheet('characters.png', 16)
sheet.draw(4, x, y)
The quads are built once, when the sheet is made, so drawing a frame never allocates.
Animation
A sequence of a sheet's frames played against a clock. Built with new Animation(sheet, frames, secondsPerFrame, [mode]).
walk = new Animation(sheet, [4, 5, 6, 7], 0.16)
function update(dt) {
walk.update(dt) // or walk.update() to use the last frame's time
}
function draw() {
walk.draw(x, y)
}
| Method | What it does |
|---|---|
update([dt]) | Advances the playhead. |
draw(x, y, [rotation, sx, sy, ox, oy]) | Draws the current frame. |
play() / pause() / resume() / stop() / reset() | Playback control. |
seek(seconds) | Jumps to a point in the sequence. |
clone() | A copy with its own playhead. |
isPlaying() / isPaused() / isFinished() | State. |
getFrame() / getFrameCount() | Which frame, and how many. |
getElapsed() / getLength() | Time into the sequence, and its total. |
getSheet() | The Spritesheet it came from. |
getDuration() / setDuration(n) | Seconds per frame. |
getSpeed() / setSpeed(n) | Playback rate; -1 runs it backwards. |
getMode() / setMode(name) | 'loop' (the default), 'once', or 'pingpong'. |
secondsPerFrame is what it says, so a walk cycle runs at the same speed on a machine managing 30 FPS as on one managing 144. Counting draws instead is the obvious version of this, and is wrong.A duration of 0 holds the first frame, which is how a still pose is written. 'once' stops on the last frame and sets isFinished(); 'loop' and 'pingpong' never finish.
Each animation carries its own playhead, so two characters showing the same walk cycle need one each — clone() is the cheap way to get one. A game that already keeps its own clock can skip update() and call seek(elapsed) instead.
Quad
Quad is exported by canvas rather than by this module, since it describes a region of a render surface rather than an image asset. getQuad() above hands one back, and import { Quad } from "lumen:canvas" builds one directly.