Codeboard.

Animation

Codeboard supports drawing substitutions, layer keyframes, and timed panel sequences. Timeline positions are global, zero-based integer frames. At 24 fps, 48 frames last two seconds.

Arrange panels

const shot = project.addScene('Street').addShot('Discovery');
const first = shot.addPanel({ id: 'notice', title: 'Notice the light', durationFrames: 48 });
const second = shot.addPanel({ id: 'reach', title: 'Reach toward it', durationFrames: 72 });

Panels follow each other in shot order. Here the first panel occupies frames 0–47 and the second 48–119. Add action, dialogue, camera notes, and general notes with panel options or panel.revise(...).

Frame-by-frame drawings

Create a group for a drawing track, with one child group or layer for each pose:

const track = first.addGroup('Hand drawings');
const relaxed = first.addGroup('Relaxed', {}, track.id);
const reaching = first.addGroup('Reaching', {}, track.id);
// Draw each pose in layers inside its group.
project.production.setDrawingSequence(track.id, [
  { frame: 0, drawingId: relaxed.id },
  { frame: 12, drawingId: reaching.id },
  { frame: 36, drawingId: relaxed.id },
]);

A drawing holds until the next key. drawingId: null creates a blank. Before the first exposure, the track is blank. A child drawing may combine vector lines and raster paint. Separate tracks can animate hands, eyes, or wings independently.

To replace a frame range while preserving the surrounding exposures:

project.production.setDrawingRange(track.id, 18, 24, relaxed.id);
console.log(project.production.drawingSequence(track.id));
console.log(project.production.drawingNeighbors(track.id, 20));

The end frame is exclusive. Drawing substitutions switch artwork; they do not morph one drawing into another. Use onion skins to compare poses.

Animate a layer

const startKey = project.production.addLayerKeyframe(track.id, 0, {
  transform: { x: 0 }, easing: 'ease-in-out',
});
project.production.addLayerKeyframe(track.id, 36, { transform: { x: 100 } });
project.production.addLayerKeyframe(track.id, 0, { opacity: 1 });
project.production.addLayerKeyframe(track.id, 36, { opacity: .5 });

Position, scale, rotation, opacity, and depth have independent channels. An opacity key leaves position unchanged. Unkeyed channels use the layer's base values. Before or after a channel's keys, its nearest keyed value holds.

Easing belongs to the key at the start of a segment. Choose linear, ease-in-out, hold, or a cubic Bézier object such as { type: 'cubic-bezier', x1: .2, y1: 0, x2: .8, y2: 1 }.

Read, move, or remove keys

console.log(project.production.layerKeyframes(track.id, { limit: 20 }));
project.production.updateLayerKeyframe(track.id, startKey, { frame: 2 });
project.production.removeLayerKeyframe(track.id, startKey);

Adding a key on an occupied frame merges the supplied properties into that key. Keep key IDs for targeted updates.

Change the pace

project.production.setPanelDuration(first.id, 60, 'ripple');

Ripple retiming changes the panel duration, remaps its interior animation times, and shifts following panels and associated timing. Audio start positions move with their timeline placement; audio samples are not time-stretched. Review sync after retiming. Codeboard does not silently shorten a later shot to retain the old total duration.

Transitions and stroke reveal

project.production.setTransition(first.id, { type: 'dissolve', durationFrames: 8 });

Available transitions are cut, dissolve, wipe-left, and wipe-right. For a drawn-on stroke, supply reveal: { startFrame: 0, endFrame: 24 } when creating a raster stroke. Pen sample timestamps alone do not animate a stroke.

On this page