Skip to content

Viking sprite

A 16-frame character sheet (idle, walk, attack, death — one row each) packed into a 4×4 grid, loaded from /img/viking.png and drawn with sprite(...).

image(src) is the source value — a path to an image, or an inline data:image/png;base64,… URI for small sheets — and sprite(image(src), width:, height:, cols:, rows:, frame:) is the displayable textured quad. cols/rows describe the atlas grid; frame selects a cell (0 = top-left, row-major) and is floored, so an animated frame: steps crisply rather than cross-fading.

Here frame: steps across all 16 cells, so the viking runs through every action — idle, walk, attack, death. The keyframes hold the first cell to introduce him and the last (the fall) before fading out via an animated opacity:. Because this sheet has a solid white cell background and its frames aren’t grid-aligned, key: #ffffff cuts out the background and anchor: center re-centres each frame’s content so the figure stays put instead of wandering.

rendering…
runner "0.0.1";

use std.shapes.*;
use std.anim.*;

scene viking_sprite(duration: Duration = 4.5s) -> Frame {
  let bg = rect(width: 1920px, height: 1080px, fill: #ffffff);

  // Hold the first cell to introduce the viking, step through all 16 cells
  // (4×4: idle, walk, attack, death), then hold the last cell before the fade.
  let cycle = animate {
    0s   => 0,
    0.7s => 0,
    2.3s => 15,
    2.9s => 15,
  };

  // Fade in to introduce, hold, fade out by 3.5s, then stay blank white for
  // ~1s before the loop restarts.
  let fade = animate {
    0s   => 0,
    0.3s => 1,
    2.9s => 1,
    3.5s => 0,
    4.5s => 0,
  };

  // The sheet's background is already transparent, so no key: is needed;
  // anchor: re-centres each frame's content; opacity: drives the fade.
  let viking = sprite(
    image("/img/viking-transparent.png"),
    width: 900px, height: 900px,
    cols: 4, rows: 4, frame: cycle, anchor: center, opacity: fade,
  );

  compose [bg, viking]
}