Synchronise your plugin with the internote animation timeline — time-based, cut-based, and pause/resume behaviour
Internotes have two animation modes. In time-based scenes, elements are revealed and animated along a millisecond timeline — students press play and watch. In cut-based scenes, students step through discrete states manually by scrolling through the page. The SDK delivers the current timeline state to your plugin so it can keep its rendering in sync with both modes.
The animation system is powered by the same cue, cue.draw, and cue.morph elements used by built-in diagram elements. When you wrap a plugin element in cue{}, the host applies a fade to the frame at the right time — and separately, the SDK delivers the timeline state so your plugin can also respond from inside.
Every animation state update delivers an object with four fields:
A scene is time-based when at least one cue{} or cue.draw{} element uses a time attribute (in, out, at). In time-based scenes, cutIndex is null and time advances in real time.
A scene is cut-based when every cue uses step-through navigation (no time attribute). In cut-based scenes, time is not meaningful and cutIndex increments each time the student presses forward.
Registers a handler that fires whenever the timeline state changes: playback starts or pauses, time advances significantly, a cut advances, or the scene is restarted.
handler (function; required). Called with an AnimationState object.voidA synchronous getter for the most recently delivered animation state. Returns null before the first state message arrives — typically only possible if accessed before onInitialize fires.
AnimationState | nullIn time-based scenes, time lets you drive your simulation or animation to match a specific position in the timeline. This is useful for seeking behaviour — if a student jumps forward on the timeline, your plugin can skip its state forward to match rather than playing through every intermediate frame.
time is in milliseconds. If your plugin's first cue{} fires at 500 ms, you can use time < 500 to detect that the plugin is not yet visible and skip expensive computation during that window.
In cut-based scenes, cutIndex is the index of the current step the student is on. Zero-indexed: the first cut is cutIndex: 0, the second is cutIndex: 1, and so on.
Design your plugin to map cut indices to internal states:
The number of cut states you define in your plugin should match the number of cue{} blocks the educator places in the Chalk scene. The educator controls the step count; your plugin maps the index to its visual output. Document the expected number of steps in your plugin description.
restartCounter increments each time the student replays the scene (using the restart button in the playback controls). Watch it and reset your simulation to its initial state to avoid carrying stale data from a previous run.
The most common pattern: run a requestAnimationFrame loop and use animation state to control pause, seek, and restart behaviour.
Animation state is also available in InitContext.animation at load time. Use it to initialise your plugin to the correct state when a student opens an internote mid-scene — for example, if the scene is set to autoplay: true and the student navigated back.
The internote animation system uses two easing curves that you can mirror in your plugin for visual consistency:
t => t < 0.5 ? 4*t*t*t : 1 - Math.pow(-2*t+2, 3) / 2t => tBoth correspond to the easing attribute on cue, cue.draw, and cue.morph elements. If your plugin animates a state transition in response to a cut, using the same easing as the surrounding cue elements produces a coherent feel.