motion artifact has body_format application/vnd.allternit.motion+json.
The body is data only; nothing in it is run as code. Create and update it
through the Artifacts API or the artifact_create and
artifact_update tools.
Body
Props by type
Text is limited to 400 characters per field. The parser fills defaults, clamps
numbers to the limits above, and keeps going on bad values, so a hand-written or
model-written body still plays. It refuses a body that isn’t JSON, has no
scenes, or has a
version newer than it knows. The model-side tools refuse a
body without a non-empty scenes array.
Renderer
renderFrame(ctx, composition, t, assets?) draws the frame at time t seconds.
It is a pure function: it reads no clock and no random numbers and keeps no
state between calls. The preview, the scrubber, the reduced-motion poster frame
and the video export all call it, so the same t gives the same picture.
- Playback only moves
tforward from a frame timer. What is drawn never depends on that timer. - Motion per object: text rises out of a mask word by word, numbers count up on an ease-out curve, bars grow one after another, lines draw along their length, logos scale in with a small overshoot, list rows slide in in turn.
- A transition draws the previous scene’s last frame, then the incoming scene over it (faded, clipped by a moving edge, or slid in).
- Logo images are decoded before drawing. A logo that fails to load shows its name as a wordmark.
Export
Frames are drawn by seeking: framei is drawn at t = i / fps.
Exported videos have no audio track.
Cloud render
There is one renderer. The host runs the app’s ownrenderFrame
(src/lib/artifacts/motion/render.ts) bundled for Node: it draws each frame
into an @napi-rs/canvas canvas and pipes raw frames to ffmpeg, which writes
H.264 (yuv420p, faststart) at the composition’s frame rate and size. The
bundle and its runtime package live together in
cmd/allternit-cloud-api/render/motion/. The bundle is checked in and
regenerated with node scripts/build-motion-render.mjs --ai <allternit-ai>
(see that folder’s README); dist/SOURCE.json records the allternit-ai commit
it came from.
All routes need the same sign-in as the rest of /api/v2/artifacts. A caller
needs at least view access to the artifact; a job belongs to the person who
started it, and other callers get 404.
Start a job
POST /api/v2/artifacts/{id}/render
version is optional. Without it the job renders the version the caller sees
(the latest for editors and the owner, the shared version for everyone else).
Only editors and the owner may ask for another version. Answers 202 with the
job:
Read a job
GET /api/v2/artifacts/{id}/render/{job}
Returns the same object. status is queued, running, done, failed,
canceled or expired. progress runs from 0 to 1; ahead counts the jobs
queued or running before this one. When status is failed, error is
{ "code", "message" }. When it is done, file_url is the path of the file
and expires_at is 24 hours after it finished. Poll about every 1.5 seconds.
Download
GET /api/v2/artifacts/{id}/render/{job}/file
Streams video/mp4 as an attachment. 409 render_not_ready while the job is
unfinished; 410 render_expired after the 24 hours.
Cancel
DELETE /api/v2/artifacts/{id}/render/{job}
Cancels a queued or running job (the renderer process is stopped), or deletes
a finished file early. Answers 204.
Limits and errors
A job can also end
failed with render_timeout, render_failed,
render_too_large (the output file) or render_interrupted (the service
restarted while it ran).
Storage and host setup
Finished files go to the private R2 bucket whenALLTERNIT_R2_* is set
(key motion-render/{user}/{job}.mp4), otherwise to the host’s temp folder.
A sweep every ten minutes deletes files 24 hours after they finished. Jobs are
rows in motion_render_jobs (migration 087_motion_render_jobs.sql).
deploy-contabo.sh ships the render folder with the binary and runs
npm ci --omit=dev into /opt/allternit-cloud-api/render/motion. Settings:
ALLTERNIT_MOTION_RENDER_DIR, ALLTERNIT_NODE_BIN (default /usr/bin/node),
ALLTERNIT_FFMPEG_BIN (default /usr/bin/ffmpeg) and
ALLTERNIT_MOTION_RENDER_TMP.
Logos given as https addresses are not fetched by the server; those scenes
draw the wordmark. Embedded data:image/... logos are drawn. Fonts are the
bundled Inter, Source Serif 4 and JetBrains Mono.