Skip to content

@gameable/conversation ​

Interfaces ​

ActionTracks ​

A clip as the service sends it: parent-local rotations per bone, the hips' path.

Properties ​

bones ​
ts
bones: Record<string, number[]>;

Per bone name, x y z w per frame.

fps ​
ts
fps: number;
frames ​
ts
frames: number;
prompt? ​
ts
optional prompt?: string;
root? ​
ts
optional root?: number[];

The hips' path, x y z per frame; absent for a clip that stands where it is.

space? ​
ts
optional space?: string;

Whose joints the clip names, when the service or a carry says (soma: the studio's standard rig).


ConversationCharacter ​

Story configuration is host-owned; game code names only a character id.

Properties ​

prefabs? ​
ts
optional prefabs?: Readonly<Record<string, readonly SpeechChunk[] | undefined>>;

This character's prepared performances, selected by the service.

storyId ​
ts
storyId: string;

The story whose snapshots are this character's. Empty: the one the relay names in its hello.

url ​
ts
url: string;

ConversationModule ​

Session control. Ending an interview retains its session and story revision.

Extends ​

Properties ​

id ​
ts
readonly id: string;

Unique id. Also the service id when init returns a service.

Inherited from ​

EngineModule.id

order? ​
ts
readonly optional order?: number;

Sort key. Lower runs first; equal keys keep registration order.

Rough convention: input -100, gameplay -50, physics 0, rendering helpers 200. Gameplay runs before physics so that the commands a guest tick emits are simulated by the step that follows rather than the next one.

Inherited from ​

EngineModule.order

Methods ​

ask() ​
ts
ask(text): boolean;
Parameters ​
ParameterType
textstring
Returns ​

boolean

beginFrame()? ​
ts
optional beginFrame(): void;

Run before the frame's fixed steps. Input capture lives here.

Returns ​

void

Inherited from ​

EngineModule.beginFrame

dispose() ​
ts
dispose(): void;

Release everything this module took.

Returns ​

void

Inherited from ​

EngineModule.dispose

end() ​
ts
end(): void;
Returns ​

void

endFrame()? ​
ts
optional endFrame(): void;

Run after rendering. Input's end-of-frame bookkeeping lives here.

Returns ​

void

Inherited from ​

EngineModule.endFrame

fixedUpdate()? ​
ts
optional fixedUpdate(dt): void;

Run once per fixed step.

Parameters ​
ParameterTypeDescription
dtnumberAlways ctx.config.fixedDt, whatever time.timeScale is; time scale changes how many steps a frame runs, not their length.
Returns ​

void

Inherited from ​

EngineModule.fixedUpdate

init() ​
ts
init(ctx): void | object | Promise<void | object>;

Acquire resources.

Parameters ​
ParameterTypeDescription
ctxHostContextThe host surface.
Returns ​

void | object | Promise<void | object>

Nothing, or the service to publish under id.

Inherited from ​

EngineModule.init

interrupt() ​
ts
interrupt(): void;

Stop the character talking, here and at the service.

Returns ​

void

say() ​
ts
say(text): boolean;

Have the character say this line word for word (the service's TextOut): a verdict the game decided, a scripted beat. It is spoken in the character's voice and kept in the conversation's history.

Parameters ​
ParameterType
textstring
Returns ​

boolean

False when there is no open session or the text is empty or too long.

start() ​
ts
start(character, options?): void;

Open a conversation. A character met before resumes its session (its story where it was), unless fresh: then the service starts the story over from its first phase.

Parameters ​
ParameterType
characterstring
options?{ fresh?: boolean; }
options.fresh?boolean
Returns ​

void

update()? ​
ts
optional update(dt, alpha): void;

Run once per frame, after the fixed steps and before rendering.

Parameters ​
ParameterTypeDescription
dtnumberClamped wall-clock seconds since the previous frame, scaled by time.timeScale.
alphanumberInterpolation factor in [0, 1) for smoothing transforms.
Returns ​

void

Inherited from ​

EngineModule.update


ConversationOptions ​

Optional conversation dependencies and callbacks.

Properties ​

characters ​
ts
characters: Readonly<Record<string, ConversationCharacter | undefined>>;
player ​
ts
player: SpeechPlayer;
prefabs? ​
ts
optional prefabs?: Readonly<Record<string, readonly SpeechChunk[] | undefined>>;

Prepared performances keyed by Convorcher prefab id; no story mutations.

Methods ​

connect()? ​
ts
optional connect(url): ConversationSocket;
Parameters ​
ParameterType
urlstring
Returns ​

ConversationSocket

onAction()? ​
ts
optional onAction(clip): void;

A motion that was still being made when its sentence came (its action marked pending) has come, as a pre-made one, or will not (an ActionTracks message): the page plays it with its sentence as the studio does.

Parameters ​
ParameterType
clipServerClip
Returns ​

void

onError() ​
ts
onError(message, detail?): void;

Something went wrong. retryable: the session is still open and the same question can be asked again (a reply that took too long, a service error); false: the session closed and start must be called again. A game must not count either as the player's attempt.

Parameters ​
ParameterType
messagestring
detail?{ retryable: boolean; }
detail.retryable?boolean
Returns ​

void

onHello()? ​
ts
optional onHello(data): void;

The session's hello (features.gesture_gen says whether the service sends its own action clips).

Parameters ​
ParameterType
dataReadonly<Record<string, unknown>>
Returns ​

void

onStatus() ​
ts
onStatus(status): void;
Parameters ​
ParameterType
statusstring
Returns ​

void

onStory() ​
ts
onStory(character, snapshot): void;
Parameters ​
ParameterType
characterstring
snapshotStorySnapshot
Returns ​

void


ConversationSocket ​

Browser connection surface; injectable for service replay tests.

Properties ​

onclose ​
ts
onclose: ((event) => void) | null;
onerror ​
ts
onerror: ((event) => void) | null;
onmessage ​
ts
onmessage: ((event) => void) | null;
onopen ​
ts
onopen: ((event) => void) | null;
readyState ​
ts
readyState: number;

Methods ​

close() ​
ts
close(): void;
Returns ​

void

send() ​
ts
send(text): void;
Parameters ​
ParameterType
textstring
Returns ​

void


FaceFrame ​

Face frames, with seconds relative to their audio chunk: 52 weights in ARKit-52 order, or, when the session asked the conversation service for the head model's own face (a2f=isaac-gnm), its 383 expression codes.

Properties ​

blendshapeWeights ​
ts
blendshapeWeights: readonly number[];
timeCode ​
ts
timeCode: number;

GnmVoice ​

The voice model's published information, which turns a native frame's codes into head coefficients: coefficient = scale x (code - offset), held within limit (see parseGnmVoice).

Properties ​

limit? ​
ts
optional limit?: Float32Array<ArrayBufferLike>;

Per coefficient, GNM_CLAMP_STD x |std x scale| (std: the spread the voice model was trained with). Past it the head model's lips cross and the splats on them tear, so each coefficient is held inside. Absent when the file carries no std.

offset ​
ts
offset: Float32Array;
scale ​
ts
scale: Float32Array;

ScheduledSentence ​

A sentence as the player scheduled it: its loudness ahead of time, on the audio clock.

Properties ​

at ​
ts
at: number;

When it starts, on clock.

clock ​
ts
clock: () => number;

The audio clock the sentence is scheduled on.

Returns ​

number

levels ​
ts
levels: Float32Array;

Its loudness, 0..1, one per step seconds (voiceLoudness).

sentenceNum ​
ts
sentenceNum: number;
step ​
ts
step: number;

Seconds per level (1/50).

text ​
ts
text: string;

ServerAction ​

One action of a reply.

Properties ​

actionText ​
ts
actionText: string;

The words the action came from.

id? ​
ts
optional id?: string;
name ​
ts
name: string;
pending? ​
ts
optional pending?: boolean;

Its clip is still being made: it comes in an ActionTracks message with this id.

performance ​
ts
performance: boolean;

A whole-body motion (a jump, a dance) rather than a small gesture under the talk.

tracks ​
ts
tracks: ActionTracks | null;

The clip; null for a stock gesture, and for one still being made (pending).


ServerClip ​

What came of a motion that was still being made (an ActionTracks message).

Properties ​

action ​
ts
action: ServerAction;

The action, with its clip when ready (tracks null otherwise).

id ​
ts
id: string;
sentenceNum ​
ts
sentenceNum: number;
status ​
ts
status: "ready" | "preset" | "dropped";

SpeechChunk ​

One bounded speech performance, already decoded from the service wire format.

Properties ​

actions? ​
ts
optional actions?: readonly ServerAction[];

The actions the service sent with this sentence (its own clips, or stock tags), as the studio reads them.

frames ​
ts
frames: readonly FaceFrame[];
gesture? ​
ts
optional gesture?: string;

A prepared performance's gesture, or the first action's name (the older road).

pcm ​
ts
pcm: Uint8Array;
sentenceNum? ​
ts
optional sentenceNum?: number;

The sentence's number in its reply (what an ActionTracks message names).

text ​
ts
text: string;

SpeechPlayer ​

Audio-clock speech queue; update reuses a single facial vector.

Methods ​

clock() ​
ts
clock(): number;

The audio clock the sentences are scheduled on, seconds.

Returns ​

number

dispose() ​
ts
dispose(): void;
Returns ​

void

enqueue() ​
ts
enqueue(chunk): void;
Parameters ​
ParameterType
chunkSpeechChunk
Returns ​

void

level() ​
ts
level(): number;

The voice's loudness right now, 0..1 (the studio's level meter).

Returns ​

number

levelAhead() ​
ts
levelAhead(seconds): number;

The loudness seconds from now, 0..1: a sentence's whole loudness is scheduled before it plays.

Parameters ​
ParameterType
secondsnumber
Returns ​

number

stop() ​
ts
stop(): void;
Returns ​

void

update() ​
ts
update(): void;
Returns ​

void


SpeechPlayerOptions ​

Options for createSpeechPlayer.

Properties ​

gnmVoice? ​
ts
optional gnmVoice?: GnmVoice | (() => GnmVoice | null);

Take native frames: the parsed voice information (parseGnmVoice), or a getter while it loads (null then: the audio plays and the face rests). Without it a 383-wide chunk is refused, as it always was.


SpeechPresentation ​

Presentation callbacks run on the audio clock.

Methods ​

actions()? ​
ts
optional actions(actions, sentenceNum): void;

The sentence's actions, the moment its voice starts (the studio plays a reply's clip as its sentence is spoken, never ahead of the words it goes with).

Parameters ​
ParameterType
actionsreadonly ServerAction[]
sentenceNumnumber
Returns ​

void

expression() ​
ts
expression(weights, space): void;

The face now. space says what weights holds: 52 ARKit weights in [0, 1] (arkit52), or, from native frames, the head's 387 gnm coefficients (the character bridge takes both as they are: characters.setExpression(entity, space, weights)). A presentation that only drives ARKit may ignore space: a player made without gnmVoice never sends gnm.

Parameters ​
ParameterType
weightsFloat32Array
spaceSpeechFaceSpace
Returns ​

void

gesture() ​
ts
gesture(id): void;
Parameters ​
ParameterType
idstring
Returns ​

void

reset() ​
ts
reset(): void;
Returns ​

void

scheduled()? ​
ts
optional scheduled(sentence): void;

A sentence was scheduled: its whole loudness and its words are known before it plays (the studio plans the talking gestures from the two at this moment).

Parameters ​
ParameterType
sentenceScheduledSentence
Returns ​

void

speaking() ​
ts
speaking(active): void;
Parameters ​
ParameterType
activeboolean
Returns ​

void

subtitle() ​
ts
subtitle(text): void;
Parameters ​
ParameterType
textstring
Returns ​

void


StorySnapshot ​

Stable full-state message emitted by Convorcher after analysis and on join.

Properties ​

completedObjectives ​
ts
completedObjectives: string[];
currentPhase ​
ts
currentPhase: string;
playerState ​
ts
playerState: Record<string, string>;
revision ​
ts
revision: number;
schemaVersion ​
ts
schemaVersion: 1;
sessionId ​
ts
sessionId: string;
storyId ​
ts
storyId: string;
transitionCause ​
ts
transitionCause: string;

TalkCommand ​

A command from the game (conversation.command), as the engine adapter hands it on.

Properties ​

action ​
ts
action: 
  | "start"
  | "end"
  | "ask"
  | "interrupt"
  | "microphone-on"
  | "microphone-off";
character ​
ts
character: string;
entity ​
ts
entity: number;
text ​
ts
text: string;

TalkEvent ​

A conversation event for the game, as the host's event queue takes it.

Properties ​

tag ​
ts
tag: "conversation-event";
val ​
ts
val: object;
entity ​
ts
entity: number;
kind ​
ts
kind: "status" | "error" | "input" | "story" | "subtitle";
text ​
ts
text: string;

TalkingCharacterBridge ​

The part of the page's character bridge a talking character uses (gameable/host/characters).

Methods ​

entryOf()? ​
ts
optional entryOf(entity): 
  | {
  animator?:   | {
     gesture: {
        play: void;
        stop: void;
     };
   }
     | null;
  clips: readonly string[];
}
  | undefined;
Parameters ​
ParameterType
entitynumber
Returns ​

| { animator?: | { gesture: { play: void; stop: void; }; } | null; clips: readonly string[]; } | undefined

setExpression() ​
ts
setExpression(
   entity, 
   space, 
   weights
): void;
Parameters ​
ParameterType
entitynumber
spaceSpeechFaceSpace
weightsFloat32Array
Returns ​

void


TalkingCharacters ​

The page's handle on its talking characters.

Methods ​

dispose() ​
ts
dispose(): void;

Close the conversation, stop the microphone and remove the talk box.

Returns ​

void

handle() ​
ts
handle(command): void;

Run one of the game's commands: conversation: (command) => talk.handle(command) in the engine adapter.

Parameters ​
ParameterType
commandTalkCommand
Returns ​

void

input() ​
ts
input(text): void;

Give the game something the player said, as the talk box does (an input event).

Parameters ​
ParameterType
textstring
Returns ​

void

say() ​
ts
say(text): boolean;

Have the open conversation's character say this line word for word.

Parameters ​
ParameterType
textstring
Returns ​

boolean


TalkingCharactersOptions ​

What talkingCharacters needs.

Properties ​

audio ​
ts
audio: object;

The engine's audio module (engine.get('audio')): the voice plays on its voice bus.

buses ​
ts
buses: object;
buses.voice ​
ts
voice: AudioNode;
context ​
ts
context: AudioContext;
characters? ​
ts
optional characters?: TalkingCharacterBridge;

The page's character bridge, for the face and gestures. Without it the character only speaks.

connect? ​
ts
optional connect?: (url) => ConversationSocket;

Open the socket another way (tests).

Parameters ​
ParameterType
urlstring
Returns ​

ConversationSocket

names? ​
ts
optional names?: Readonly<Record<string, string>>;

The name the talk box shows for each character id (elora: 'Elora'). An id not named here is shown as itself; the relay decides which ids exist (its src/<id>.story.yaml files).

panel? ​
ts
optional panel?: boolean | HTMLElement;

The talk box: a subtitle line and a field to type (and a button to speak) into, shown while a conversation is open. true (the default) puts it at the bottom of the page; an element puts it in there; false leaves the page's own UI to call TalkingCharacters.input.

url? ​
ts
optional url?: string | URL;

The relay, as a URL the page can reach. Default: talk/ under the page's own address.

Methods ​

emit() ​
ts
emit(event): void;

Where events for the game go: usually (event) => adapter.events.push(event).

Parameters ​
ParameterType
eventTalkEvent
Returns ​

void

Type Aliases ​

SpeechFaceSpace ​

ts
type SpeechFaceSpace = "arkit52" | "gnm";

What a face vector holds: ARKit-52 weights, or the head model's native gnm coefficients.

Variables ​

ARKIT_FRAME_WIDTH ​

ts
const ARKIT_FRAME_WIDTH: 52 = 52;

ARKit weights in one frame.


GNM_CLAMP_STD ​

ts
const GNM_CLAMP_STD: 3 = 3;

How many standard deviations of the voice model's training spread a coefficient may reach.


GNM_FRAME_WIDTH ​

ts
const GNM_FRAME_WIDTH: 383 = 383;

The head model's expression codes in one native frame (a2f=isaac-gnm).


GNM_SPACE_DIM ​

ts
const GNM_SPACE_DIM: 387 = 387;

The gnm expression space: the 383 coefficients, then a four-wide gaze tail the player leaves at zero.


LEVEL_FPS ​

ts
const LEVEL_FPS: 50 = 50;

The loudness meter's rate: fifty levels a second (the studio's LEVEL_FPS).


LEVEL_STEP ​

ts
const LEVEL_STEP: number;

Seconds per loudness level.

Functions ​

acceptStorySnapshot() ​

ts
function acceptStorySnapshot(
   current, 
   next, 
   sessionId, 
   storyId
): StorySnapshot | null;

Idempotent reducer scoped to an expected session and story.

Parameters ​

ParameterTypeDescription
currentStorySnapshot | nullLast accepted snapshot.
nextStorySnapshotCandidate snapshot.
sessionIdstringExpected session.
storyIdstringExpected story.

Returns ​

StorySnapshot | null

The accepted snapshot, or the previous state.


asActionTracks() ​

ts
function asActionTracks(v): ActionTracks | null;

A clip as sent, or null when it is not one.

Parameters ​

ParameterTypeDescription
vunknownThe tracks field.

Returns ​

ActionTracks | null

The clip, or null.

Example ​

ts
import { asActionTracks } from 'gameable/conversation';
console.log(asActionTracks({ fps: 30, frames: 2, bones: { pelvis: [0, 0, 0, 1, 0, 0, 0, 1] } })?.frames); // 2

clipFitsRig() ​

ts
function clipFitsRig(tracks, joints): boolean;

Does a clip address this rig's own joints, by their names? Half the clip's joints found is a fit; a clip that names none fits (nothing to judge by). The studio's clipFitsRig.

Parameters ​

ParameterTypeDescription
tracksActionTracksThe clip.
jointsreadonly string[]The rig's joint names.

Returns ​

boolean

Whether it fits.

Example ​

ts
import { clipFitsRig } from 'gameable/conversation';
console.log(clipFitsRig({ fps: 30, frames: 2, bones: { pelvis: [] } }, ['Hips'])); // false

conversation() ​

ts
function conversation(options): ConversationModule;

Build an optional session manager. Construction does not connect to any service.

Parameters ​

ParameterTypeDescription
optionsConversationOptionsExplicit character allow-list, playback and callbacks.

Returns ​

ConversationModule

An engine module. Call start only after an interview is requested.


createSpeechPlayer() ​

ts
function createSpeechPlayer(
   context, 
   presentation, 
   destination?, 
   options?
): SpeechPlayer;

Build a bounded PCM player. The caller creates/resumes the context in a user gesture.

Frames 52 wide play as ARKit-52 weights. Frames 383 wide (the head model's own face, a2f=isaac-gnm) play as the head's gnm coefficients when options.gnmVoice is given: coefficient = scale x (code - offset), held within GNM_CLAMP_STD x |std x scale|, interpolated at the frames' own times and eased in and out at the ends of a run of speech; without it they are refused.

Parameters ​

ParameterTypeDefault valueDescription
contextAudioContextundefinedShared or dedicated audio clock.
presentationSpeechPresentationundefinedSubtitles, gestures and facial rig.
destinationAudioNodecontext.destinationVoice bus; defaults to the context destination.
optionsSpeechPlayerOptions{}Native frames (gnmVoice).

Returns ​

SpeechPlayer

A player which owns only its sources, not the context.


faceSpaceOf() ​

ts
function faceSpaceOf(width, native): SpeechFaceSpace | null;

The space a chunk's frames drive, from their width.

Parameters ​

ParameterTypeDescription
widthnumberNumbers per frame.
nativebooleanWhether the player takes native frames (it was given gnmVoice).

Returns ​

SpeechFaceSpace | null

arkit52 for 52, gnm for 383 when native frames are taken; null for anything else (refused).


gnmCoefficients() ​

ts
function gnmCoefficients(
   codes, 
   voice, 
   out?
): Float32Array;

One native frame's codes as head coefficients, each held within the voice's limit, with a zero gaze tail.

Parameters ​

ParameterTypeDescription
codesArrayLike<number>The frame's 383 codes as the service sends them.
voiceGnmVoiceThe voice information.
outFloat32ArrayOutput of length GNM_SPACE_DIM, reused.

Returns ​

Float32Array

out.


parseActions() ​

ts
function parseActions(raw): ServerAction[];

data.animations of a TextOut, whatever came: never throws, drops what is not an action.

Parameters ​

ParameterTypeDescription
rawunknownThe field.

Returns ​

ServerAction[]

The actions, in order.

Example ​

ts
import { parseActions } from 'gameable/conversation';
console.log(parseActions([{ Name: 'Smile', ActionText: 'smiles' }])[0].name); // Smile

parseClip() ​

ts
function parseClip(raw): ServerClip | null;

An ActionTracks message's data, whatever came: null when it is not one.

Parameters ​

ParameterTypeDescription
rawunknownThe field.

Returns ​

ServerClip | null

The clip's arrival, or null.

Example ​

ts
import { parseClip } from 'gameable/conversation';
console.log(parseClip({ id: 'm1', sentenceNum: 0, status: 'dropped' })?.status); // dropped

parseGnmVoice() ​

ts
function parseGnmVoice(raw): GnmVoice | null;

Read a voice information file (gnm_voice.json: scale, offset and optionally std, 383 numbers each).

Parameters ​

ParameterTypeDescription
rawunknownThe parsed JSON.

Returns ​

GnmVoice | null

The voice, with limit when the file carries a valid std; null when it cannot be used.


parseStorySnapshot() ​

ts
function parseStorySnapshot(value): StorySnapshot | null;

Validate a bounded, versioned story snapshot.

Parameters ​

ParameterTypeDescription
valueunknownUntrusted message data.

Returns ​

StorySnapshot | null

Owned snapshot, or null for invalid data.


serverActsLive() ​

ts
function serverActsLive(hello): boolean;

Does this session's hello say the service finds the actions itself? Only a plain true counts.

Parameters ​

ParameterTypeDescription
hellounknownThe session message's data.

Returns ​

boolean

Whether features.gesture_gen is true.

Example ​

ts
import { serverActsLive } from 'gameable/conversation';
console.log(serverActsLive({ features: { gesture_gen: true } })); // true

talkingCharacters() ​

ts
function talkingCharacters(options): TalkingCharacters;

Wire talking characters to the page and the game.

Parameters ​

ParameterTypeDescription
optionsTalkingCharactersOptionsThe audio, the character bridge, where game events go, and the characters' names.

Returns ​

TalkingCharacters

The handle the engine adapter's conversation option calls.

Example ​

ts
import { talkingCharacters } from 'gameable/conversation';

declare const engine: { get(id: 'audio'): { context: AudioContext; buses: { voice: AudioNode } } };
declare const events: { tag: 'conversation-event'; val: { entity: number; kind: 'input' | 'status' | 'subtitle' | 'error' | 'story'; text: string } }[];

const talk = talkingCharacters({
  audio: engine.get('audio'),
  emit: (event) => events.push(event),
  names: { elora: 'Elora' },
});
// createEngineAdapter(engine, { ..., conversation: (command) => talk.handle(command) })

toPcm16() ​

ts
function toPcm16(
   chunks, 
   samples, 
   rate
): Uint8Array;

Float samples at rate to 16 kHz mono little-endian PCM16.

Parameters ​

ParameterTypeDescription
chunksreadonly Float32Array<ArrayBufferLike>[]The recorded blocks.
samplesnumberHow many samples they hold.
ratenumberTheir sample rate.

Returns ​

Uint8Array

PCM16 bytes.


travels() ​

ts
function travels(tracks, min?): boolean;

Does the clip go somewhere? (start to end over the floor, metres): the studio's travels.

Parameters ​

ParameterTypeDefault valueDescription
tracksActionTracksundefinedThe clip.
minnumber0.6Metres that count as travel.

Returns ​

boolean

Whether it travels.

Example ​

ts
import { travels } from 'gameable/conversation';
console.log(travels({ fps: 30, frames: 2, bones: { Hips: [] }, root: [0, 1, 0, 2, 1, 0] })); // true

voiceLoudness() ​

ts
function voiceLoudness(pcm): Float32Array;

A sentence's loudness from its sound, fifty a second (the studio's loudness): the RMS of each 20 ms, on the meter's own scale (0 at a whisper's floor, 1 at a raised voice).

Parameters ​

ParameterTypeDescription
pcmUint8ArrayMono PCM16 at 16 kHz, little-endian.

Returns ​

Float32Array

One level per 20 ms, 0..1.

Example ​

ts
import { voiceLoudness } from 'gameable/conversation';
console.log(voiceLoudness(new Uint8Array(3200)).length); // 5