Skip to main content
decktalk init writes a deck that already builds, and copying one of its pages is the shortest route to a second one. This guide is for the other case: a page written from an empty file, because the deck belongs to a design that neither packaged example fits. Everything here is the same contract the starter obeys. A [data-scene] wrapper is one scene, each <template data-slide> inside it is one slide, an element has four moments and each moment is a cue name. No attribute says when a cue fires, because cues.json owns that.

The skeleton

deck/mine.html
That is a complete page. It lists its scene when opened with no query, plays it with ?scene=1, and records under the cue times a run resolves.

Loading the runtime and KaTeX

DeckTalk serves two things itself, under /__decktalk/ on the origin it opens every page at: decktalk-runtime.js, the compiled runtime, and katex/, a pinned KaTeX release with its stylesheet and its font files. Both come from the installed engine, so the project holds no copy of either. Load them with the absolute paths above, which resolve the same from any directory a page sits in. KaTeX goes before the runtime, so the first slide is already typeset when the recorder starts the clock. The runtime waits for a typesetter a page asks for, and a page with data-tex on it and no KaTeX at all earns PAGE_KATEX_MISSING. A page with no equations needs neither line.
Load nothing from a network address. A recording that fetched a font, a stylesheet or a highlighter from somebody else’s server is a film that depends on a host it does not own, which is PAGE_CDN_ASSET. Copy the asset into deck/ instead, which is what the lesson example does with its bundled OFL fonts.

The canvas

The runtime builds a canvas element, #dt-canvas, of the frame’s shape with a short side of 1080 CSS pixels, 1920 by 1080 for a wide project, and scales it to fit the window. It puts the canvas’s size on :root as --dt-width and --dt-height and its shape as data-dt-shape, so a stylesheet that reads them lays the page out for a square or vertical frame as well. A page that declares its own <div id="dt-canvas"> keeps that element and the runtime builds around it, which is how a deck puts one background under all of its slides. Style it, and do not size it.

The scene wrapper

deck/mine.html
data-scene is the id a [[section]] in decktalk.toml names in its scene key. data-name is the scene’s name in the index page and in the catalog, and a scene with no name is called “Scene” and its id. A wrapper holding no <template data-slide> is PAGE_SCENE_EMPTY. A moment attribute written on the wrapper itself, outside every template, has no slide to qualify it, so it is PAGE_MOMENT_UNKNOWN.

The slide templates

A slide is a <template> the runtime clones when the slide mounts. Four attributes belong to it.
deck/mine.html
Use data-enter="cut" where the picture changes completely, because a crossfade there reads as a dissolve rather than as a new idea. data-describe on the template is the slide’s own subject, and data-describe on an element inside it is that element’s. Four rules the reader checks before a pixel is drawn. Design a slide covers the element attributes, and Slide recipes has a pattern for each thing they do.

The DeckTalk object

decktalk-runtime.js publishes window.DeckTalk, which is everything a page may call. A page written only in markup calls none of it. window.__decktalk is the same page read back: the mode, the scene, the slide, the cues, the warnings and the measured catalog. The recorder and every check read the page through it, and a page never writes to it.

A cue a handler serves

deck/mine.html
DeckTalk.on takes a cue id, because a handler may be registered anywhere in the deck and nothing around the call says which slide it means. A slide’s own owns and on keys are cue names, because the slide they belong to already says which slide that is.
A handler receives the mounted slide element, or null when no slide is mounted, and a context of id, the cue id of the cue, at, the second on the page clock, frozen, whether the page is a still, and slideId. A handler that throws earns PAGE_HANDLER_THREW, and the moment does nothing rather than the page failing. List in data-owns every cue a handler serves that no moment attribute names. That is what makes the slide the owner of the cue, and ownership is what decides when the slide is mounted. A cue in cues.json that no slide owns is PAGE_NO_OWNER, and a cue that fires and finds neither a moment nor a handler is PAGE_CUE_UNKNOWN.

A slide built in code

DeckTalk.scene declares a scene whose slides build their own markup. A scene declared in script and a [data-scene] wrapper with the same id are one scene, so a deck may be half markup and half code, and a slide that defines render keeps it rather than taking the markup of a template with its id.
deck/mine.html
A render returns the slide’s inner HTML and is given the scene, the slide and whether the page is frozen. It throws into PAGE_RENDER_THREW, entered throws into PAGE_ENTER_THREW and a handler in on throws into PAGE_SLIDE_HANDLER_THREW, and in every case the slide is drawn as its markup stands rather than the page failing.
Inside the backticks of a render, every backslash has to be written twice, because the markup is a JavaScript string. In a <template> a backslash is written once. That alone is reason enough to write an equation as a template.

Waiting for something of your own

deck/mine.html
DeckTalk.waitFor holds the page’s readiness until the promise settles, so the recorder does not start the clock on a page that is still loading. The budget is five seconds. A promise that rejects is PAGE_WAIT_REJECTED, one that never settles is PAGE_WAIT_UNSETTLED, and the page is drawn either way, so a missing file never hangs a build.

The query keys the runtime reads

One URL decides which of four things the page is doing: it lists its scenes, it previews one, it plays one against the section clock, or it freezes one slide. The query keys lists every key and what it asks for. after and before are answered by the probe that decktalk check and decktalk storyboard inject into the page. A page you open yourself with ?freeze=1.1 freezes that slide with every one of its cues fired, and a cue those two keys name that the slide does not declare is PAGE_FREEZE_CUE_UNKNOWN on the page’s own console. No command reports it, because storyboard draws a panel only for a cue the slide declares.

Open the page yourself

The recorder opens every page over http, and decktalk serve is the one supported way to open it yourself.
A page opened from a file:// URL draws nothing. The browser resolves /__decktalk/decktalk-runtime.js against the root of the disk, finds no file there, and the page has no runtime to mount its slides. It serves the deck directory and the files decktalk.toml declares, and nothing else. It prints where the deck is served as one object, flushed at once, and then serves until you stop it. Its output stays open while it serves, so a caller reads the object as it arrives rather than waiting for the stream to end. --host and --port choose the interface and the port, and a port of 0 takes any free one. The deck directory is every directory a page sits in, so every name under it is served however the page spells it. A file outside it, such as a clip, a words file or a sound, is served only by the spelling decktalk.toml gives it. Write the same name in the page, down to its case, because a request for Media/Intro.mp4 is refused when the project declared media/intro.mp4, on every platform alike. The recorder opens the page under the same rule, so a page that previews cleanly records cleanly. Open the page by its path under that origin, such as http://127.0.0.1:56257/deck/mine.html:
  • The page with no query lists every scene and every slide, and links a preview and a still of each.
  • deck/mine.html?scene=1 previews that scene. Once decktalk cue has run, the preview asks the origin for the cue times that run resolved and plays the film’s own timing. With none to find, it fires each cue a second apart so a deck whose cues have never been resolved still shows its whole self.
  • deck/mine.html?scene=1&speed=4&hud=1 reviews a long scene quickly with the clock and the cue list on screen.
  • deck/mine.html?freeze=1.1 freezes one slide with its cues fired.

Put the page in the project

decktalk.toml
The section number, the scene its [[section]] names and the slide ids of its cues agree, which is the shape every DeckTalk project follows. Then write the section’s narration in script.md and one row per moment in cues.json, and judge the three together:

Next

  • Make each reveal survive a check: Design a slide
  • Copy a pattern that already ships: Slide recipes
  • Look up every attribute and finding: run decktalk schema page