Spritesheets

Animate a static sprite into a spritesheet driven by a motion text prompt (image-to-spritesheet): supply an initial_image (URL or base64) plus a motion_prompt like "walking" or "attack slash", and optionally a final_image to interpolate between a start and end frame. Describe the motion exactly and unambiguously, but do not over-describe it: an action the model already knows is one phrase, not a sequence of steps, and the character, art style, background and camera come from the image, not the prompt. For animations driven by up to three keyframes (including a middle frame), use animateSpriteKeyframes instead. The job result is a single sprite result: `spritesheet_url` (the sheet image), `video_url` (an mp4 of the animation - pass it as `video` to transferMotion or as `spritesheet_video_url` to createSpriteAudio; editSpritesheet takes `spritesheet_url`), `num_frames`/`num_cols`/`num_rows` (the grid layout), and, when requested, `gif_url`, `individual_frame_urls` and `spritesheet_with_background_url`. The hydra and hydra-plus models also return `audio_url`, a sound effect for the animation - there is no need to call createSpriteAudio afterwards; forge and forge-pixel produce no audio. The chosen model must support sprite animation and the duration must be valid for it; incompatible model/duration combinations return HTTP 400. Credits are held for the duration you requested when the job is accepted; the final charge is rate × seconds of video generated (legacy models keep their minimum charge), never more than for the duration you requested, and the difference (or everything, if the job fails or is cancelled) is refunded. The returned sheet is then trimmed to its best loop and can run slightly shorter than the video; that trim is not deducted. Choosing a tool: animateSprite (this one) when you can describe the motion in text; transferMotion when you want an exact motion copied from a reference video or one of the named presets from listAnimationPresets (e.g. a standard walk or attack cycle); generatePose first when the source image is not yet in the pose the animation should start from. Omit `model` to run on hydra (the default) - see the `model` field for the per-model rates. Pass an optional request_id to tag the result so you can locate it later via listGenerations (type spritesheet). Requires an API key (user scope). The call returns 202 with a job id - poll getApiJob (pass wait 30 to long-poll) honouring poll_after_ms, until status is succeeded; its result field is exactly the response documented for this operation. Synchronous responses are deprecated but still supported: pass `async: false` to block until the result is ready and receive it as the response body. Synchronous calls run on the same queue: the response carries an X-Ludo-Job-Id header, and a job still running after 15 minutes comes back as 202 with the job instead of an error. Each account may have up to 50 generations queued or running at once via the API; a request beyond that returns 429 (code PENDING_JOBS_LIMIT).

Changed on
post/assets/sprite/animate

Request

  • Base URL
    https://api.ludo.ai/api
  • Auth
    API key in header Authorization

Request body

motion_promptstring required

Text description of the desired animation, e.g. "walking", "idle breathing", "attack slash", "casting a fireball with both hands". Describe exactly and unambiguously what the character should do. Specific or complex actions are fine; there is no need to simplify them. Do not over-describe, though: too much detail can harm the animation, and an action the model already knows should be named, not decomposed into its steps ("walking", never "move the left foot forward, then the right foot"). Do not restate what initial_image already shows (the character, its appearance and equipment, the art style, the background, the lighting, the camera), and leave frame count and timing to the frames and duration fields. Negative phrasing ("no background", "do not move the camera", "without a weapon") works only on hydra and hydra-plus. On forge, forge-pixel and the legacy models it backfires: naming something you do not want makes it more likely to appear, so "without a weapon" tends to produce a weapon. On those models never phrase anything negatively; state only what should happen, and control everything else through the image and the other fields. Keep it well under 100 words: in longer prompts details start competing and some get dropped.

initial_imagestring required

The url OR base64 of the starting frame image to animate. This is the base sprite that will be brought to life.

final_imagestring

The url OR base64 of ending frame image. When provided, the animation will interpolate between the initial and final frames.

loopboolean

Trim the animation at the beginning or end to create a seamless loop. Not guaranteed to produce a perfect loop.

cropboolean

Crop sprite frames to fit content. Results in smaller spritesheets but inconsistent frame sizes across different animations.

frames4 | 9 | 16 | 25 | 36 | 49 | 64

Maximum number of frames in the output spritesheet. Allow about one second of duration per 16 frames - above that the model may not be able to produce them all (64 frames needs a duration of at least 4). The result can have fewer frames when the duration is short or bad frames were removed.

frame_size32 | 64 | 96 | 128 | 192 | 256 | 384 | 0 | -1 | -9

Size of the sprite in pixels, not of the frame: it sets how big the character is drawn in the exported sheet, and each frame also holds the space around the sprite, so frames come out larger than this value. It changes the export only, not how the sprite is generated. 0 is maximum resolution. -1 is AI 1.5x upscaling. -9 is True Size: frames keep the size and position of the input frame, untrimmed, so the spritesheet lines up exactly with the input image - use it (not margin_ratio_mode "none") whenever the output must align with your input; it works with any margin mode. Exporting larger than the input image adds no detail: do not pick a size above the input's own resolution, and 0 or -1 gain nothing on inputs under 512 px. True Size crops at the input image's edges, so anything the animation adds beyond them (an aura, a spell effect, a swung weapon) is cut off: use a fixed size or 0 for those. adjustSpritesheet re-exports a finished animation at another size for free.

margin_rationumber float

Deprecated: prefer margin_ratio_horizontal / margin_ratio_vertical. Amount of padding around the sprite as a ratio (0.0 to 1.0). Sets both axes to this value. A per-axis value, when also given, overrides this for that axis. Supplying any margin value selects margin_ratio_mode "manual" unless margin_ratio_mode is set explicitly.

margin_ratio_horizontalnumber float

Horizontal padding around the sprite as a ratio (0.0 to 1.0). Every bit of margin is resolution taken from the sprite, so keep it as tight as the motion allows - about 0.15 - and go higher only for motion that extends sideways (sword slashes, punches) or if the sprite gets cut off. Supplying a value selects margin_ratio_mode "manual" unless margin_ratio_mode is set explicitly; overrides the legacy margin_ratio on this axis.

margin_ratio_verticalnumber float

Vertical padding around the sprite as a ratio (0.0 to 1.0). Every bit of margin is resolution taken from the sprite, so keep it as tight as the motion allows - about 0.15 - and go higher only for motion that extends up or down (jumps) or if the sprite gets cut off. Supplying a value selects margin_ratio_mode "manual" unless margin_ratio_mode is set explicitly; overrides the legacy margin_ratio on this axis.

margin_ratio_mode'auto' | 'manual' | 'none'

How the sprite is framed for generation. The model always generates on a canvas of the same size, so any margin is canvas the sprite does not fill: the more margin, the lower the resolution and detail of the sprite. "auto" (default, recommended) trims and centers the sprite and picks a margin suited to the motion. "manual" uses margin_ratio_horizontal / margin_ratio_vertical; keep them as tight as the motion allows (about 0.15) and raise only the axis the motion needs. "none" animates the input exactly as framed, with no trimming and no margin added, so the input must already have suitable margins: a sprite touching the edges gets its limbs, weapons and effects cut off as soon as it moves, and an image that is mostly empty space yields a small, low-detail sprite. "none" is the way to keep a deliberately off-center sprite (e.g. a character at the left edge firing a beam to the right), but results can be unpredictable. Do not use "none" to make the spritesheet line up with the input image - use frame_size -9 (True Size) for that; it works with any margin mode. Rules: omit margin_ratio_mode and send margin_ratio_horizontal / margin_ratio_vertical to get "manual" automatically; "manual" with no margin value fails with HTTP 400; "auto" or "none" sent together with a margin value fails with HTTP 400 (the value would be ignored).

image_typestring

Type of sprite being animated. Affects generation parameters and styling. Tiling types (horizontal / vertical tiles, scrolling backgrounds, parallax layers, textures) stay seamless only on forge and forge-pixel, and only if the input image itself already wraps seamlessly on that axis; otherwise the animation shows a visible seam when tiled.

model'hydra' | 'hydra-plus' | 'griffin-hd' | 'forge' | 'forge-pixel' | 'blitz' | 'eagle' | 'eagle-audio' | 'standard'

Animation model to use. hydra is the most capable and generates audio; forge is best for basic animations and relatively simple sprites; forge-pixel for pixel art. blitz (default), eagle and eagle-audio are legacy models kept for existing integrations.

durationnumber float

Animation length in seconds. Available values depend on the model - the source of truth is MODEL_PRICING (credits.js), surfaced via /credits/costs; the public API docs list them per model (auto-generated).

augment_promptboolean

Rewrites your prompt behind the scenes into the form the model works best with. Leave it on (the default). Turning it off does not give you more control - it usually gives worse results. Do not disable it unless you really know what you are doing and have tested your prompts extensively.

gifboolean

When true, generates an animated GIF from the spritesheet and returns it in gif_url. Disabled by default to reduce response time.

individual_framesboolean

When true, extracts each frame from the spritesheet as an individual image and returns the URLs in individual_frame_urls.

spritesheet_with_backgroundboolean

When true, also returns the spritesheet with background intact (before background removal). Useful for manually fixing background removal issues. The with-background spritesheet URL will be in spritesheet_with_background_url.

use_pixel_gridboolean

Use the animation's pixel grid if one is detected. Defaults to true. true: when Ludo detects a pixel grid with confidence, the sheet is packed on it (logical pixels, scaled up by whole blocks); when none is detected, this has no effect. false: always pack as ordinary art, for when the detected grid is wrong for your asset (for example a smooth sprite coming back as coarse blocks). Ignored for forge-pixel animations, which are always packed on their grid.

request_idstring

Optional client-provided identifier, unique per request. Re-sending the same request_id returns the existing job instead of generating again. Also usable as the request_id filter of listGenerations.

asyncboolean

Defaults to true: the call returns 202 immediately with a job id to poll with getApiJob. Set false for a synchronous response (deprecated but supported): the call then blocks until the result is ready.

Response

Success

spritesheet_urlstring
video_urlstring
audio_urlstring

URL of the sound effect generated for the animation (MP3 or WAV; the extension says which). hydra and hydra-plus only; absent for forge and forge-pixel.

gif_urlstring
individual_frame_urlsstring[]
num_framesinteger

Number of frames in the spritesheet.

num_colsinteger

Columns in the frame grid. Frames run left-to-right, top-to-bottom.

num_rowsinteger

Rows in the frame grid. The last row may be only partly filled.

spritesheet_with_background_urlstring
individual_frame_with_background_urlsstring[]
durationnumber float
loopboolean

The loop setting the sheet was made with. false is a one-shot animation (an attack, a death) that should play once in an engine.

out_of_frameboolean

true when Ludo detected the sprite running past the edges of the frame during the animation, so parts of it are cut off. It is a hint, not a guarantee: look at the frames. The cut is in the generated video, so adjustSpritesheet can't recover it. Call editSpritesheet with edit_mode "outpaint" to extend the frame beyond its borders, or generate again with a larger margin on the axis that was cut (margin_ratio_horizontal / margin_ratio_vertical).

bad_loopboolean

true when Ludo detected that the animation doesn't loop cleanly. It is a hint, not a guarantee. Call editSpritesheet with edit_mode "fix_loop" to repair the loop.

request_idstring
created_atinteger

Changes