Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/TextAliveJp/textalive-app-api/llms.txt

Use this file to discover all available pages before exploring further.

The hierarchy

When a song is loaded, TextAlive parses the lyrics and builds a tree of rendering units. Each node in the tree has a startTime and endTime (in milliseconds) derived from the vocalization-timing analysis.
IVideo
└── IPhrase[]          "Hello world,"
    └── IWord[]        "Hello"  "world,"
        └── IChar[]    "H" "e" "l" "l" "o"   ","
You access the root via player.video (type IVideo).

IRenderingUnit — the base interface

Every node in the hierarchy implements IRenderingUnit.
interface IRenderingUnit extends TimedObject {
  readonly startTime: number;
  readonly endTime: number;
  readonly duration: number;       // endTime - startTime [ms]
  readonly parent: IRenderingUnit;
  readonly children: IRenderingUnit[];
  readonly previous: IRenderingUnit;
  readonly next: IRenderingUnit;

  progress(time: number): number;  // maps time → [0, 1] within this unit
  getType(): number;               // see UnitTypes

  // Custom animation override (see below)
  animate: RenderingUnitFunction;
}

UnitTypes

getType() returns one of the numeric constants in UnitTypes:
ConstantValueMeaning
UnitTypes.PHRASE1IPhrase
UnitTypes.WORD2IWord
UnitTypes.CHAR3IChar
UnitTypes.GRAPHIC4Graphic unit
import { UnitTypes } from "textalive-app-api";

if (unit.getType() === UnitTypes.CHAR) {
  console.log((unit as IChar).text);
}

The animate property

When you assign a function to a unit’s animate property, TextAlive’s default template animation for that unit is suppressed and your function is called instead on every render frame while the unit is active.
type RenderingUnitFunction = (now: number, unit: IRenderingUnit) => void;
player.addListener({
  onVideoReady(video) {
    // Override animation for every character
    video.chars.forEach((char) => {
      char.animate = (now, u) => {
        const t = u.progress(now); // 0 → 1
        myRender(u as IChar, t);
      };
    });
  },
});
Setting animate on a unit prevents TextAlive’s built-in template from rendering that unit. Remove the assignment (set to null) to restore default behaviour.

IVideo

IVideo is the root container. You access it via player.video.
interface IVideo extends TimedObject {
  readonly phrases: IPhrase[];
  readonly words: IWord[];          // flat list across all phrases
  readonly chars: IChar[];          // flat list across all words

  readonly phraseCount: number;
  readonly wordCount: number;
  readonly charCount: number;

  readonly firstPhrase: IPhrase;
  readonly lastPhrase: IPhrase;
  readonly firstWord: IWord;
  readonly lastWord: IWord;
  readonly firstChar: IChar;
  readonly lastChar: IChar;

  getPhrase(index: number): IPhrase;
  getWord(index: number): IWord;
  getChar(index: number): IChar;

  findPhrase(time: number, options?: FindTimedObjectOptions): IPhrase;
  findWord(time: number, options?: FindTimedObjectOptions): IWord;
  findChar(time: number, options?: FindTimedObjectOptions): IChar;

  findPhraseChange(startTime: number, endTime: number): TimedObjectsInRange<IPhrase>;
  findWordChange(startTime: number, endTime: number): TimedObjectsInRange<IWord>;
  findCharChange(startTime: number, endTime: number): TimedObjectsInRange<IChar>;

  progress(time: number): number;
}
The find*Change methods return a TimedObjectsInRange object which tells you exactly what changed in a given time window — which units started (entered), which ended (left), and which is currently active (current). This is the recommended pattern inside onTimeUpdate.

IPhrase

A IPhrase represents a sung line or grouping of words that are vocalized together.
interface IPhrase extends IRenderingUnit {
  readonly text: string;         // full text of the phrase
  readonly startTime: number;
  readonly endTime: number;
  readonly children: IWord[];    // words in this phrase
  readonly wordCount: number;
  readonly charCount: number;
  readonly firstWord: IWord;
  readonly lastWord: IWord;
  readonly firstChar: IChar;
  readonly lastChar: IChar;
  readonly previous: IPhrase;
  readonly next: IPhrase;
}

IWord

A IWord represents a single word within a phrase. Each word knows its part-of-speech and the language it belongs to.
interface IWord extends IRenderingUnit {
  readonly text: string;
  readonly parent: IPhrase;
  readonly children: IChar[];    // characters in this word
  readonly charCount: number;
  readonly firstChar: IChar;
  readonly lastChar: IChar;
  readonly previous: IWord;
  readonly next: IWord;

  // Part-of-speech (normalised)
  // N=Noun, PN=ProNoun, V=Verb, R=adveRb, J=adJective, A=Adnominal,
  // P=Particle, M=Modal, W=Wh, D=Determiner, I=conjunction,
  // U=Interjection, F=preFix, S=Symbol, X=other
  readonly pos: string;

  // Raw POS tag from NLTK (English) or MeCab (Japanese)
  readonly rawPos: string;

  // Language code: "en" or "ja"
  readonly language: string;
}
Part-of-speech codes at a glance:
CodeNameCodeName
NNounPParticle
PNProNounMModal
VVerbWWh-word
RAdverbDDeterminer
JAdjectiveUInterjection
AAdnominalFPrefix
IConjunctionSSymbol
XOther

IChar

A IChar is the finest-grained unit — a single character. In addition to timing, each char carries visual styling properties.
interface IChar extends IRenderingUnit {
  readonly text: string;
  readonly parent: IWord;
  readonly previous: IChar;
  readonly next: IChar;

  // Visual properties (read/write)
  font: IFont;
  fontFamily: string;
  fontStyle: string;
  fontSize: number;     // px
  color: IColor;        // RGBA color
}

Examples

Display the current character

player.addListener({
  onTimeUpdate(position) {
    const char = player.video.findChar(position);
    if (char) {
      myDisplay.textContent = char.text;
      // char.progress(position) gives 0→1 across its duration
    }
  },
});

Detect when a new phrase starts

let prevPosition = 0;

player.addListener({
  onTimeUpdate(position) {
    const changes = player.video.findPhraseChange(prevPosition, position);

    changes.entered.forEach((phrase) => {
      console.log("Phrase started:", phrase.text);
    });

    changes.left.forEach((phrase) => {
      console.log("Phrase ended:", phrase.text);
    });

    prevPosition = position;
  },
});

Walk all characters at startup

player.addListener({
  onVideoReady(video) {
    for (const phrase of video.phrases) {
      for (const word of phrase.children) {
        for (const char of word.children) {
          console.log(
            `${char.text} [${char.startTime}ms – ${char.endTime}ms]`
          );
        }
      }
    }
  },
});

Highlight nouns differently

player.addListener({
  onTimeUpdate(position) {
    const word = player.video.findWord(position);
    if (word && word.pos === "N") {
      highlight(word);
    }
  },
});

Full type references: /api/video · /api/phrase · /api/word · /api/char