Document reference
Every tag, motion, anchor and hook of a VisoTalk document, spec 0.5.
The spec is 0.5, a draft: the format may change in any minor version. The types are the source of truth
(spec/ir/0.5/ir.ts), and visotalk schema prints the vocabulary below from the
validator itself (schema tags, schema motions, schema hooks; --json for data). This page is the same list with the reasons.
How a video is written is the walk-through.
A document
A document is a TSX module with two readers. The skeleton (Doc, Scene, Line, Part, Animation and the motion tags) is read by the engine as text and
never run, so it is data: strings, numbers, booleans, lists and objects, and no variables or loops in it. The inside of a <Part> is ordinary React,
run by the player and the exporter, and may use anything in the module above export default.
- A string attribute is read as JSX reads it, with no escapes: put a value with a quote or a backslash in braces (
text={"say \"hi\""}). A list always takes braces (on={["a", "b"]}). A target is referred to by its id as a string. - Every id (document, scene, line, part, target) starts with a lowercase letter, then
a-z,0-9,_and-:bar-0, neverbarZero. - In a line's words write
<>&for<>&, and{}for{}. Comments are{/* … */}.
Tags
Attributes in [ ] are optional.
| Tag | Attributes | What it is |
|---|---|---|
Doc | id title lang [description poster sources generator created updated spec theme tokens voice speed lexicon canvas pacing exports] | The root. Its children are Canvas, Asset, Part (global: it outlives the scenes) and Scene |
Scene | id [title duration] | Lines, parts, one Animation and any Sounds. A scene without lines needs duration (seconds) |
Line | id [text hold i18n] | The words spoken are its children, and they are the caption. text is the caption when it differs (digits for numbers spoken as words); hold is silence after the line. <Mark id="x">phrase</Mark> names a phrase an action can start on |
Part | id [x y w h z] | A box (default the whole canvas) holding React: elements, or a function that returns them (hooks run inside it). Invisible until an Appear. In a Scene it ends with the scene; in Doc it stays |
Animation | The actions of the scene, in any order | |
Sound | asset + where it starts [id dur volume loop fadeIn fadeOut duck] | Music or an effect of an <Asset kind="audio">, in a Scene. volume and duck are dB, fadeIn, fadeOut and dur seconds. loop without dur lasts to the end of the film; fadeOut needs dur or loop; the narration is never changed |
Asset | id kind src license [source analysis proxy] | A file the document uses (kind is image, video, audio, font or data), read in a part with useAsset(id). Every asset carries its licence and source; the credits are generated from them |
On Doc, canvas="16x9" (or "9x16") sets the canvas, theme and tokens the colours and fonts, pacing the silences (lead, hold, tail, 0.4, 0.4 and 0.8 seconds),
and texts={{id: '…'}} the words a person edited. voice, speed and lexicon are in the CLI page.
exports takes variants (a list of canvas presets), fps (30 by default) and captions: burned on the picture, sidecar as captions.srt, both, none, or
words, the spoken words in the lower half of a tall frame.
Where an action starts
Exactly one of the following; offset={seconds} adds to any of them.
| Form | Starts |
|---|---|
line="l1" word="phrase" | At the start of that phrase in the line's speech; edge="end" for the end of it. The phrase is any text the line says, case and punctuation aside. nth={2} picks the second occurrence. In Chinese, Japanese and Thai a phrase is any run of characters |
line="l1" mark="name" | At the phrase a <Mark id="name"> wraps in that line; edge="end" for its end |
line="l1" | At the start of the line; edge="end" for its end |
after="id" | When the action with that id ends (edge="start": when it begins). A chain must begin on a word, a scene time or a scene edge |
at={1.5} | Seconds into the scene |
at="start" or at="end" | The scene's edges |
Motions
| Motion | Attributes | What it does |
|---|---|---|
Appear | on + where it starts [dur stagger easing id] | Shows a target (default 0.45 s). Every part is invisible until an Appear, and so is any other target whose first action is an appear |
Disappear | on + where it starts [dur stagger easing id] | Hides it (default 0.3 s) |
Tween | on + where it starts [dur stagger easing repeat yoyo id] + props | Moves props to new values over dur (default 0.6 s), from the value they have then. Props are numbers, colours and lengths. repeat plays it again (-1: for the whole film) and yoyo plays every other time backwards. x, y (px), scale and rotate (deg) move the target itself |
Set | on + where it starts [id] + props | Changes props at once: numbers, strings, true and false |
on is one id or a list; with stagger={0.1} the targets of a list start 0.1 s apart. The props of a Tween or Set are its other attributes, and none may be named like
an attribute of the action itself (on, id, dur, stagger, easing, repeat, yoyo, at, after, line, word, nth, mark, edge, offset).
easing is linear, easeIn, easeOut or easeInOut (cubic curves), those with Sine, Quad, Quart, Quint or Back after them (easeOutQuart,
easeOutBack(2.2) overshoots by 2.2), or cubic-bezier(x1, y1, x2, y2).
An action's target is a part, or an element a part registers with <Obj id> or useTarget. Ids are unique within a scene. A scene's actions reach only its own parts' targets;
a global part's targets are reached by every scene's actions.
Hooks
These work only inside a part's React, and come from @visotalk/runtime.
| Hook | What it gives |
|---|---|
useTarget(id) | Props to spread on the element that is the target id (data-vt and a style: opacity, visibility, transform). Merge your own style over it, never replace it |
<Obj id as? style?> | A div (or as="g", "span") that is a target: useTarget in a tag |
useProp(name, base) | A prop of the part it runs in that Tween and Set change: its value now, base before the first. useProp(id, name, base) is the same for the target id |
useText(id, base) | Words a person may edit: the text written in <Doc texts> for this id, or base until there is one |
useTime() | Seconds since the scene on screen started: draw by it, never by a timer |
useDocTime() | Seconds since the start of the video |
useFrame() | The frame the clock is on |
useCanvas() | {width, height, preset}: the canvas it is drawn on |
useTheme() | The theme tokens {colors, fonts, radius, space}, also as CSS variables (var(--vt-data), var(--vt-sans)) |
useAsset(id) | The URL of an <Asset> |
useScenes() | The scenes as {id, from, end} in seconds, for a picture that spans them (a chapter rail) |
useSounds() | The <Sound>s as {asset, start, end?} in seconds, for a picture that answers a sound (a flash on a hit) |
random(key) | A deterministic random generator: the same key gives the same numbers |
<Video asset in? start? rate? fit?> | A clip of an <Asset kind="video">, played from in seconds of the source at rate, starting start seconds into the scene. Its sound is not used |
A frame is a function of the clock, so validate refuses Math.random, Date, timers, fetch and CSS animations or transitions in a part
(code.nondeterministic, with what to use instead).
What validate reports
Every issue is one line at doc.tsx:line:col with a code, most with a fix; --json gives them as data.
| Code | Means |
|---|---|
id.format, id.duplicate | An id that breaks the rule above, or one used twice |
part.appear | A part nothing appears |
anchor.word | The phrase is not in the line |
anchor.ambiguous | The phrase occurs twice; the issue lists every place, so write a longer one or use nth |
anchor.action, anchor.cycle, anchor.after-negative | An after that names no action, a cycle of actions that follow one another, or an offset that puts an action before the one it follows |
action.id | An action id that is not unique in the document |
code.nondeterministic | A part uses something a frame may not depend on |
target.unregistered | An animation names a target no part registers; it stops render |
qa adds what only a rendered frame shows: text over text (text-overlap), text cut by a shape (text-crossed), ink past the canvas or under the captions, an object on screen for under two
seconds (visible.brief), a target registered twice (target.duplicate), and anchors resting on an estimated or weak alignment (align.estimated, align.weak).