VisoTalk
$npx skills add visotalk/visotalk -g
docs

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, never barZero.
  • In a line's words write &lt; &gt; &amp; for < > &, and &#123; &#125; for { }. Comments are {/* … */}.

Tags

Attributes in [ ] are optional.

TagAttributesWhat it is
Docid 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
Sceneid [title duration]Lines, parts, one Animation and any Sounds. A scene without lines needs duration (seconds)
Lineid [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
Partid [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
AnimationThe actions of the scene, in any order
Soundasset + 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
Assetid 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.

FormStarts
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

MotionAttributesWhat it does
Appearon + 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
Disappearon + where it starts [dur stagger easing id]Hides it (default 0.3 s)
Tweenon + where it starts [dur stagger easing repeat yoyo id] + propsMoves 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
Seton + where it starts [id] + propsChanges 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.

HookWhat 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.

CodeMeans
id.format, id.duplicateAn id that breaks the rule above, or one used twice
part.appearA part nothing appears
anchor.wordThe phrase is not in the line
anchor.ambiguousThe phrase occurs twice; the issue lists every place, so write a longer one or use nth
anchor.action, anchor.cycle, anchor.after-negativeAn 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.idAn action id that is not unique in the document
code.nondeterministicA part uses something a frame may not depend on
target.unregisteredAn 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).