Skip to main content


This is the text documentation. For a comprehensive personal tour through the library, check out the ractives deep dive.


The code is split into many modules and plugins.

Phases of development#

In my experience, the process of creating a ractive can be divided into three phases. (This does not include the creative phase of deciding what you want to say, which is usually the hardest part.)

  1. Authoring: This is when you write HTML/CSS/Javascript for your ractive.

  2. Recording: This is when you record audio and other interactions.

  3. Mastering: This is for the finishing touches on a video, ensuring cross-platform compatibility, etc.

The above links provide practical advice on the issues that arise in each phase.

Classes and Concepts#


This is the most important class. Effectively, a Playback mimics an audio/video element that can be played and rewinded, has volume settings, a playback rate, etc. It thus imitates the HTMLMediaElement interface to a certain extent, although it does not fully implement that interface.

Times are generally represented as numbers representing milliseconds since the beginning of playback. They can also be specified as strings like "7:35" or "7:35.66". Currently, there is no distinction between time points and time durations.


The HTMLMediaElement interface usually represents times in seconds.

Script and markers#

A Script augments a Playback by breaking it into named segments, which we call markers (cue would also be appropriate, but that conflicts with WebVTT Cues).

A marker is a triple [name: string, start: number, end: number], where start and end are times in the sense of the previous section. However, when creating a Script a marker may be specified as a pair [name: string, duration: number].

const markers = [
["intro/day", "0:09.351"],
] as [string, string][];
const script = new Script(markers);

When defining markers in the Authoring phase, you can put anything for the duration, e.g. ["intro/", "1:00"]. The durations will get filled in during the Recording phase.


A Player provides a GUI interface for playing ractives, resembling a traditional web video player.

It is important that Playback and Script do not depend on React, and could be used without Player. Eventually we will be more agnostic about templating systems, so that e.g. Vue or Custom Elements could be used instead of (or in conjunction with) React.


If an element has the data-from-first="first" attribute, it will be visible only when the current marker is equal to or comes after the marker whose name is "first". If an element further has the data-from-last="last" attribute, it will be visible only when the current marker comes strictly before the marker whose name is "last". If an element has the data-during="prefix" attribute, it will be visible only when the current marker's name begins with "prefix".

There are helper functions for this in Utils.authoring. Since you will be using these a lot, you might want to define a shortcut for them in your text editor.

import {Utils} from "ractive-player";
const {during, from} = Utils.authoring;
function IntroSlide() {
return (
<section {...during("intro/")}>
<h1 {...from("intro/", "intro/day")}>
Hello <span {...from("intro/name")}>Bob!</span>
<p {...from("intro/day")}>Have a great day!</p>

Internally, elements are hidden by setting opacity:0; pointer-events: none;, and shown by removing these attributes. This operates outside of React rendering. The reason for using these styles instead of

  • visibility: hidden; is that an invisible element can have visible children with the latter approach, whereas our approach allows us to hide an element without recursing into its children.
  • display:none; is that the latter causes reflow, which is slow. Another practical difference is that that elements hidden using our method will occupy whitespace (which may or may not be desired).

It may seem strange to render everything at the beginning and then selectively show/hide it, rather than rendering selectively based on the current time. My own use case for this library uses a lot of MathJax, which takes a few seconds to render, so selective rendering would disrupt the viewing experience. If you don't have any content with long repaints, selective rendering will probably work fine.


This is a common pattern in RP elements replaying something recorded by the author (cursor movements, typing, etc.). It is not part of the JavaScript code, but is exported from the TypeScript.

type ReplayData<K> = [number, K][];

The numbers represent durations in milliseconds. Thus, a piece of ReplayData is usually paired with a start attribute for replaying, but ReplayData itself does not involve absolute times, so it can be moved around easily.