@pinecall/dot 0.0.0-stage → 0.11.22
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +202 -0
- package/README.md +316 -2
- package/dist/agent/index.d.ts +4 -0
- package/dist/agent/index.d.ts.map +1 -0
- package/dist/agent/index.js +3 -0
- package/dist/agent/index.js.map +1 -0
- package/dist/agent/tutor.d.ts +67 -0
- package/dist/agent/tutor.d.ts.map +1 -0
- package/dist/agent/tutor.js +351 -0
- package/dist/agent/tutor.js.map +1 -0
- package/dist/face/draw.d.ts +43 -0
- package/dist/face/draw.d.ts.map +1 -0
- package/dist/face/draw.js +74 -0
- package/dist/face/draw.js.map +1 -0
- package/dist/face/ear.d.ts +23 -0
- package/dist/face/ear.d.ts.map +1 -0
- package/dist/face/ear.js +111 -0
- package/dist/face/ear.js.map +1 -0
- package/dist/face/index.d.ts +23 -0
- package/dist/face/index.d.ts.map +1 -0
- package/dist/face/index.js +138 -0
- package/dist/face/index.js.map +1 -0
- package/dist/face/motion.d.ts +25 -0
- package/dist/face/motion.d.ts.map +1 -0
- package/dist/face/motion.js +58 -0
- package/dist/face/motion.js.map +1 -0
- package/dist/face/mouth.d.ts +45 -0
- package/dist/face/mouth.d.ts.map +1 -0
- package/dist/face/mouth.js +61 -0
- package/dist/face/mouth.js.map +1 -0
- package/dist/face/shape.d.ts +15 -0
- package/dist/face/shape.d.ts.map +1 -0
- package/dist/face/shape.js +50 -0
- package/dist/face/shape.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -0
- package/dist/page/page.d.ts +34 -0
- package/dist/page/page.d.ts.map +1 -0
- package/dist/page/page.js +218 -0
- package/dist/page/page.js.map +1 -0
- package/dist/page/roles.d.ts +10 -0
- package/dist/page/roles.d.ts.map +1 -0
- package/dist/page/roles.js +87 -0
- package/dist/page/roles.js.map +1 -0
- package/dist/pointer/card.d.ts +13 -0
- package/dist/pointer/card.d.ts.map +1 -0
- package/dist/pointer/card.js +75 -0
- package/dist/pointer/card.js.map +1 -0
- package/dist/pointer/code.d.ts +21 -0
- package/dist/pointer/code.d.ts.map +1 -0
- package/dist/pointer/code.js +147 -0
- package/dist/pointer/code.js.map +1 -0
- package/dist/pointer/flight.d.ts +30 -0
- package/dist/pointer/flight.d.ts.map +1 -0
- package/dist/pointer/flight.js +92 -0
- package/dist/pointer/flight.js.map +1 -0
- package/dist/pointer/hover.d.ts +20 -0
- package/dist/pointer/hover.d.ts.map +1 -0
- package/dist/pointer/hover.js +96 -0
- package/dist/pointer/hover.js.map +1 -0
- package/dist/pointer/menu.d.ts +48 -0
- package/dist/pointer/menu.d.ts.map +1 -0
- package/dist/pointer/menu.js +252 -0
- package/dist/pointer/menu.js.map +1 -0
- package/dist/pointer/person.d.ts +91 -0
- package/dist/pointer/person.d.ts.map +1 -0
- package/dist/pointer/person.js +345 -0
- package/dist/pointer/person.js.map +1 -0
- package/dist/pointer/sections.d.ts +24 -0
- package/dist/pointer/sections.d.ts.map +1 -0
- package/dist/pointer/sections.js +64 -0
- package/dist/pointer/sections.js.map +1 -0
- package/dist/pointer/syntax.d.ts +10 -0
- package/dist/pointer/syntax.d.ts.map +1 -0
- package/dist/pointer/syntax.js +61 -0
- package/dist/pointer/syntax.js.map +1 -0
- package/dist/pointer/typing.d.ts +6 -0
- package/dist/pointer/typing.d.ts.map +1 -0
- package/dist/pointer/typing.js +62 -0
- package/dist/pointer/typing.js.map +1 -0
- package/dist/tutor/captions.d.ts +22 -0
- package/dist/tutor/captions.d.ts.map +1 -0
- package/dist/tutor/captions.js +62 -0
- package/dist/tutor/captions.js.map +1 -0
- package/dist/tutor/macro.d.ts +19 -0
- package/dist/tutor/macro.d.ts.map +1 -0
- package/dist/tutor/macro.js +67 -0
- package/dist/tutor/macro.js.map +1 -0
- package/dist/tutor/options.d.ts +42 -0
- package/dist/tutor/options.d.ts.map +1 -0
- package/dist/tutor/options.js +3 -0
- package/dist/tutor/options.js.map +1 -0
- package/dist/tutor/tutor.d.ts +14 -0
- package/dist/tutor/tutor.d.ts.map +1 -0
- package/dist/tutor/tutor.js +389 -0
- package/dist/tutor/tutor.js.map +1 -0
- package/dist/tutor/views.d.ts +27 -0
- package/dist/tutor/views.d.ts.map +1 -0
- package/dist/tutor/views.js +63 -0
- package/dist/tutor/views.js.map +1 -0
- package/dist/tutor/walking.d.ts +44 -0
- package/dist/tutor/walking.d.ts.map +1 -0
- package/dist/tutor/walking.js +142 -0
- package/dist/tutor/walking.js.map +1 -0
- package/dist/tutor/walks.d.ts +46 -0
- package/dist/tutor/walks.d.ts.map +1 -0
- package/dist/tutor/walks.js +112 -0
- package/dist/tutor/walks.js.map +1 -0
- package/dist/wire/index.d.ts +133 -0
- package/dist/wire/index.d.ts.map +1 -0
- package/dist/wire/index.js +204 -0
- package/dist/wire/index.js.map +1 -0
- package/package.json +53 -3
package/dist/face/ear.js
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/** The face's ear: an analyser on the element the agent's voice plays in, read once a frame. */
|
|
2
|
+
//
|
|
3
|
+
// The analyser is never connected to the speakers: the element already plays the voice, and the
|
|
4
|
+
// ear only measures it. So the mouth moves with the sound the visitor hears, frame by frame, and
|
|
5
|
+
// is in time with it by construction. An AudioContext may only start inside a click, so the ear
|
|
6
|
+
// is deaf until `wake()` is called from one. A recording (an element with a `src`, not a stream)
|
|
7
|
+
// is routed through the ear to the speakers, as Web Audio requires of an element it reads; it must
|
|
8
|
+
// be fetched with CORS (`crossOrigin = "anonymous"`), or the ear hears silence.
|
|
9
|
+
import { BANDS, levelOf, peakAfter } from "./mouth.js";
|
|
10
|
+
const FFT = 1024;
|
|
11
|
+
export class Ear {
|
|
12
|
+
#context = null;
|
|
13
|
+
#analyser = null;
|
|
14
|
+
#source = null;
|
|
15
|
+
/** An element's own source node: Web Audio makes one per element, once. */
|
|
16
|
+
#played = new WeakMap();
|
|
17
|
+
#element = null;
|
|
18
|
+
#time = new Float32Array(FFT);
|
|
19
|
+
#spectrum = new Float32Array(FFT / 2);
|
|
20
|
+
#peak = -18;
|
|
21
|
+
/** Start listening. Call it from the click that starts the call; a page with no Web Audio stays deaf. */
|
|
22
|
+
wake() {
|
|
23
|
+
if (this.#context === null) {
|
|
24
|
+
if (typeof AudioContext === "undefined")
|
|
25
|
+
return;
|
|
26
|
+
this.#context = new AudioContext();
|
|
27
|
+
this.#analyser = this.#context.createAnalyser();
|
|
28
|
+
this.#analyser.fftSize = FFT;
|
|
29
|
+
this.#analyser.smoothingTimeConstant = 0;
|
|
30
|
+
}
|
|
31
|
+
void this.#context.resume();
|
|
32
|
+
}
|
|
33
|
+
// The voice's track in a stream of the ear's own, as livekit-client's createAudioAnalyser takes it:
|
|
34
|
+
// the element's own stream, the one it plays, gave an analyser that heard nothing on a live call.
|
|
35
|
+
/** Listen to the voice this element plays, or to nothing. The same element twice is nothing new. */
|
|
36
|
+
hear(element) {
|
|
37
|
+
const listening = this.#source !== null || element === null;
|
|
38
|
+
if (element === this.#element && listening)
|
|
39
|
+
return;
|
|
40
|
+
// From the analyser alone: a recording's source also plays to the speakers, and keeps doing so.
|
|
41
|
+
if (this.#analyser !== null)
|
|
42
|
+
this.#source?.disconnect(this.#analyser);
|
|
43
|
+
this.#source = null;
|
|
44
|
+
this.#element = element;
|
|
45
|
+
if (this.#context === null || this.#analyser === null || element === null)
|
|
46
|
+
return;
|
|
47
|
+
const stream = element.srcObject;
|
|
48
|
+
if (stream === null && element.src !== "") {
|
|
49
|
+
this.#source = this.#recording(this.#context, element);
|
|
50
|
+
this.#source.connect(this.#analyser);
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
const track = stream instanceof MediaStream ? stream.getAudioTracks()[0] : undefined;
|
|
54
|
+
if (track === undefined)
|
|
55
|
+
return;
|
|
56
|
+
this.#source = this.#context.createMediaStreamSource(new MediaStream([track]));
|
|
57
|
+
this.#source.connect(this.#analyser);
|
|
58
|
+
}
|
|
59
|
+
/** A recording's source, made once, playing to the speakers whether or not the ear reads it now. */
|
|
60
|
+
#recording(context, element) {
|
|
61
|
+
let made = this.#played.get(element);
|
|
62
|
+
if (made === undefined) {
|
|
63
|
+
made = context.createMediaElementSource(element);
|
|
64
|
+
made.connect(context.destination);
|
|
65
|
+
this.#played.set(element, made);
|
|
66
|
+
}
|
|
67
|
+
return made;
|
|
68
|
+
}
|
|
69
|
+
/** Where the ear stands now. */
|
|
70
|
+
get hearing() {
|
|
71
|
+
if (this.#context === null)
|
|
72
|
+
return "asleep";
|
|
73
|
+
if (this.#element === null)
|
|
74
|
+
return "no voice";
|
|
75
|
+
return this.#source === null ? "no track" : "listening";
|
|
76
|
+
}
|
|
77
|
+
/** This frame of the voice, `dt` seconds after the last; null while the ear hears nothing. */
|
|
78
|
+
listen(dt) {
|
|
79
|
+
const analyser = this.#analyser;
|
|
80
|
+
if (analyser === null || this.#source === null || this.#context === null)
|
|
81
|
+
return null;
|
|
82
|
+
analyser.getFloatTimeDomainData(this.#time);
|
|
83
|
+
let sum = 0;
|
|
84
|
+
for (const sample of this.#time)
|
|
85
|
+
sum += sample * sample;
|
|
86
|
+
const db = 20 * Math.log10(Math.sqrt(sum / this.#time.length) + 1e-9);
|
|
87
|
+
this.#peak = peakAfter(this.#peak, db, dt);
|
|
88
|
+
analyser.getFloatFrequencyData(this.#spectrum);
|
|
89
|
+
const hz = this.#context.sampleRate / FFT;
|
|
90
|
+
const power = ([low, high]) => {
|
|
91
|
+
let sum = 0;
|
|
92
|
+
const last = Math.min(this.#spectrum.length - 1, Math.ceil(high / hz));
|
|
93
|
+
for (let bin = Math.floor(low / hz); bin <= last; bin += 1)
|
|
94
|
+
sum += Math.pow(10, (this.#spectrum[bin] ?? -200) / 10);
|
|
95
|
+
return sum;
|
|
96
|
+
};
|
|
97
|
+
return {
|
|
98
|
+
level: levelOf(db, this.#peak),
|
|
99
|
+
bands: { low: power(BANDS.low), mid: power(BANDS.mid), high: power(BANDS.high), sib: power(BANDS.sib) },
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
/** Stop listening for good. */
|
|
103
|
+
close() {
|
|
104
|
+
this.#source?.disconnect();
|
|
105
|
+
this.#source = null;
|
|
106
|
+
void this.#context?.close();
|
|
107
|
+
this.#context = null;
|
|
108
|
+
this.#analyser = null;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
//# sourceMappingURL=ear.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ear.js","sourceRoot":"","sources":["../../src/face/ear.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/** The face: a dot that speaks with the agent's voice, drawn into any element of the page. */
|
|
2
|
+
import type { RoomStore } from "@pinecall/room";
|
|
3
|
+
import { type Shape } from "./shape.js";
|
|
4
|
+
/** A face on the page. */
|
|
5
|
+
export interface Face {
|
|
6
|
+
/** Let the face hear the voice. Call it from the click that starts the call: a browser starts audio only inside one. */
|
|
7
|
+
wake(): void;
|
|
8
|
+
/** Change the body's shape: it eases into the new one. */
|
|
9
|
+
reshape(shape: Shape): void;
|
|
10
|
+
/** Speak with this element's audio instead of the call's voice, a recording the page plays; null gives the call back. */
|
|
11
|
+
listen(element: HTMLMediaElement | null): void;
|
|
12
|
+
/** Take the face off the page and stop listening. */
|
|
13
|
+
close(): void;
|
|
14
|
+
}
|
|
15
|
+
export interface FaceOptions {
|
|
16
|
+
/** The body's shape: `circle` (the default), `squircle` or `blob`. */
|
|
17
|
+
shape?: Shape | undefined;
|
|
18
|
+
/** The body's colour, any CSS colour; the page's `--pc-face` when left out. */
|
|
19
|
+
color?: string | undefined;
|
|
20
|
+
}
|
|
21
|
+
/** Draw a face into `host`, filling it, moving with `call`. The host sets its size. */
|
|
22
|
+
export declare function face(host: Element, call: RoomStore, options?: FaceOptions): Face;
|
|
23
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/face/index.ts"],"names":[],"mappings":"AAAA,8FAA8F;AAS9F,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAMhD,OAAO,EAAsC,KAAK,KAAK,EAAE,MAAM,YAAY,CAAC;AAE5E,0BAA0B;AAC1B,MAAM,WAAW,IAAI;IACnB,wHAAwH;IACxH,IAAI,IAAI,IAAI,CAAC;IACb,0DAA0D;IAC1D,OAAO,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;IAC5B,yHAAyH;IACzH,MAAM,CAAC,OAAO,EAAE,gBAAgB,GAAG,IAAI,GAAG,IAAI,CAAC;IAC/C,qDAAqD;IACrD,KAAK,IAAI,IAAI,CAAC;CACf;AAED,MAAM,WAAW,WAAW;IAC1B,sEAAsE;IACtE,KAAK,CAAC,EAAE,KAAK,GAAG,SAAS,CAAC;IAC1B,+EAA+E;IAC/E,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CAC5B;AAcD,uFAAuF;AACvF,wBAAgB,IAAI,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,GAAE,WAAgB,GAAG,IAAI,CAiHpF"}
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
/** The face: a dot that speaks with the agent's voice, drawn into any element of the page. */
|
|
2
|
+
//
|
|
3
|
+
// It reads the room it is given and nothing else: the phase and the agent's own state set its mood
|
|
4
|
+
// (resting, listening, hearing the caller, thinking), and while the agent's voice sounds, its mouth
|
|
5
|
+
// is read from that voice frame by frame. Its body is one of a few shapes, in the page's colour, and
|
|
6
|
+
// moves whole with the mood, its outline never bent: it bobs and leans in while it listens, nods with
|
|
7
|
+
// its head cocked and its eyes wide while the caller speaks, looks up from side to side with its
|
|
8
|
+
// mouth pursed while it thinks, and squashes with the voice. Nothing is sent anywhere and nothing is asked of the server.
|
|
9
|
+
import { drawn, painted } from "./draw.js";
|
|
10
|
+
import { Ear } from "./ear.js";
|
|
11
|
+
import { BLINK_S, POSES, SOUNDING, eased, easedMouth, lidAt, moodOf, nextBlink } from "./motion.js";
|
|
12
|
+
import { spoken } from "./mouth.js";
|
|
13
|
+
import { morphed, outlineOf, radii, shapeOf } from "./shape.js";
|
|
14
|
+
/** How high the body bobs in each mood, in the box's units, and how often, per second. */
|
|
15
|
+
const BOB = {
|
|
16
|
+
idle: { high: 0, often: 0 },
|
|
17
|
+
listening: { high: 1.5, often: 0.8 },
|
|
18
|
+
hearing: { high: 1.3, often: 1.7 },
|
|
19
|
+
thinking: { high: 0.6, often: 0.4 },
|
|
20
|
+
speaking: { high: 0, often: 0 },
|
|
21
|
+
};
|
|
22
|
+
// Without an ear (never woken, or no Web Audio) the mouth opens with the room's own level, in no shape.
|
|
23
|
+
const NO_SHAPE = { low: 1, mid: 1, high: 0, sib: 0 };
|
|
24
|
+
/** Draw a face into `host`, filling it, moving with `call`. The host sets its size. */
|
|
25
|
+
export function face(host, call, options = {}) {
|
|
26
|
+
const drawing = drawn(host, `pc-face-${Math.random().toString(36).slice(2, 10)}`);
|
|
27
|
+
if (options.color !== undefined && host instanceof HTMLElement)
|
|
28
|
+
host.style.setProperty("--pc-face", options.color);
|
|
29
|
+
let rest = radii(shapeOf(options.shape));
|
|
30
|
+
let rim = rest;
|
|
31
|
+
let bob = 0;
|
|
32
|
+
let own = null;
|
|
33
|
+
const ear = new Ear();
|
|
34
|
+
const still = typeof matchMedia === "function" && matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
35
|
+
let mouth = { ...POSES.idle };
|
|
36
|
+
let level = 0;
|
|
37
|
+
let last = performance.now();
|
|
38
|
+
let blinking = null;
|
|
39
|
+
let blinkIn = nextBlink(Math.random());
|
|
40
|
+
const gaze = { x: 0, y: 0 };
|
|
41
|
+
const glance = { x: 0, y: 0 };
|
|
42
|
+
let glanceIn = 2;
|
|
43
|
+
let frame = 0;
|
|
44
|
+
let closed = false;
|
|
45
|
+
const tick = (at) => {
|
|
46
|
+
const dt = Math.min(0.05, Math.max(0, (at - last) / 1000));
|
|
47
|
+
last = at;
|
|
48
|
+
const state = call.state;
|
|
49
|
+
ear.hear(own ?? state.voice);
|
|
50
|
+
const heard = ear.listen(dt);
|
|
51
|
+
const loud = heard?.level ?? (state.speaking.agent ? state.speaking.level : 0);
|
|
52
|
+
// What the mouth reads, on the host, for a page that wants to know: the voice itself, or the room's coarse level.
|
|
53
|
+
if (host instanceof HTMLElement) {
|
|
54
|
+
host.dataset["pcEar"] = ear.hearing;
|
|
55
|
+
host.dataset["pcLevel"] = loud.toFixed(2);
|
|
56
|
+
}
|
|
57
|
+
const mood = (state.phase === "live" || own !== null) && loud > SOUNDING ? "speaking" : moodOf(state);
|
|
58
|
+
const seconds = at / 1000;
|
|
59
|
+
// Thinking purses its mouth and slides it from side to side, "hmm".
|
|
60
|
+
const hmm = still ? 1 : Math.sin(seconds * Math.PI * 2 * 0.32);
|
|
61
|
+
const target = mood === "speaking" ? spoken(loud, heard?.bands ?? NO_SHAPE) : mood === "thinking" ? { ...POSES.thinking, shift: 3.2 * hmm } : POSES[mood];
|
|
62
|
+
mouth = easedMouth(mouth, target, dt);
|
|
63
|
+
level = eased(level, mood === "speaking" ? loud : 0, dt, 0.03, 0.12);
|
|
64
|
+
blinkIn -= dt;
|
|
65
|
+
if (blinking === null && blinkIn <= 0)
|
|
66
|
+
blinking = 0;
|
|
67
|
+
let lid = 1;
|
|
68
|
+
if (blinking !== null) {
|
|
69
|
+
blinking += dt;
|
|
70
|
+
lid = lidAt(blinking);
|
|
71
|
+
if (blinking >= BLINK_S) {
|
|
72
|
+
blinking = null;
|
|
73
|
+
blinkIn = nextBlink(Math.random());
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
// Thinking looks up, from side to side; hearing looks at the caller, toward the page; otherwise a
|
|
77
|
+
// small glance now and then, smaller while speaking.
|
|
78
|
+
glanceIn -= dt;
|
|
79
|
+
if (mood === "thinking") {
|
|
80
|
+
glance.x = 2.6 * hmm;
|
|
81
|
+
glance.y = -3.2;
|
|
82
|
+
glanceIn = 0.4;
|
|
83
|
+
}
|
|
84
|
+
else if (mood === "hearing") {
|
|
85
|
+
glance.x = -1.2;
|
|
86
|
+
glance.y = 0.6;
|
|
87
|
+
glanceIn = 0.4;
|
|
88
|
+
}
|
|
89
|
+
else if (glanceIn <= 0) {
|
|
90
|
+
glanceIn = 1.5 + Math.random() * 3;
|
|
91
|
+
const wander = mood === "speaking" ? 0.8 : 1.6;
|
|
92
|
+
glance.x = (Math.random() - 0.5) * 2 * wander;
|
|
93
|
+
glance.y = (Math.random() - 0.5) * wander;
|
|
94
|
+
}
|
|
95
|
+
gaze.x = eased(gaze.x, glance.x, dt, 0.12, 0.12);
|
|
96
|
+
gaze.y = eased(gaze.y, glance.y, dt, 0.12, 0.12);
|
|
97
|
+
const breath = still ? 0 : Math.sin(seconds * Math.PI * 2 * 0.22) * 0.018;
|
|
98
|
+
rim = morphed(rim, rest, dt);
|
|
99
|
+
bob = eased(bob, still ? 0 : BOB[mood].high, dt, 0.3, 0.4);
|
|
100
|
+
const wave = Math.sin(seconds * Math.PI * 2 * (BOB[mood].often || 0.8));
|
|
101
|
+
// A bob is a little hop: up a touch taller and narrower, down a touch wider, as a soft body does.
|
|
102
|
+
const hop = bob * wave;
|
|
103
|
+
// Hearing cocks its head, as a person does who is following; thinking sways slowly.
|
|
104
|
+
const lean = { idle: 0, listening: -5, hearing: -9, thinking: 4 + 2.5 * hmm, speaking: 0 }[mood];
|
|
105
|
+
painted(drawing, {
|
|
106
|
+
mouth,
|
|
107
|
+
lid,
|
|
108
|
+
eyes: mood === "hearing" ? 1.16 : mood === "listening" ? 1.08 : 1,
|
|
109
|
+
gaze,
|
|
110
|
+
tilt: still ? 0 : lean + Math.sin(seconds * 0.7) * 1.2,
|
|
111
|
+
lift: hop,
|
|
112
|
+
scale: { x: 1 + breath + level * 0.025 - mouth.open * 0.012 - hop * 0.008, y: 1 + breath + level * 0.02 + mouth.open * 0.03 + hop * 0.01 },
|
|
113
|
+
body: outlineOf(rim),
|
|
114
|
+
});
|
|
115
|
+
frame = requestAnimationFrame(tick);
|
|
116
|
+
};
|
|
117
|
+
tick(performance.now());
|
|
118
|
+
return {
|
|
119
|
+
wake() {
|
|
120
|
+
ear.wake();
|
|
121
|
+
},
|
|
122
|
+
reshape(shape) {
|
|
123
|
+
rest = radii(shape);
|
|
124
|
+
},
|
|
125
|
+
listen(element) {
|
|
126
|
+
own = element;
|
|
127
|
+
},
|
|
128
|
+
close() {
|
|
129
|
+
if (closed)
|
|
130
|
+
return;
|
|
131
|
+
closed = true;
|
|
132
|
+
cancelAnimationFrame(frame);
|
|
133
|
+
ear.close();
|
|
134
|
+
drawing.svg.remove();
|
|
135
|
+
},
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/face/index.ts"],"names":[],"mappings":"AAAA,8FAA8F;AAC9F,EAAE;AACF,mGAAmG;AACnG,oGAAoG;AACpG,qGAAqG;AACrG,sGAAsG;AACtG,iGAAiG;AACjG,0HAA0H;AAI1H,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAC3C,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AAC/B,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACpG,OAAO,EAAE,MAAM,EAA0B,MAAM,YAAY,CAAC;AAC5D,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,OAAO,EAAc,MAAM,YAAY,CAAC;AAqB5E,0FAA0F;AAC1F,MAAM,GAAG,GAAG;IACV,IAAI,EAAE,EAAE,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE;IAC3B,SAAS,EAAE,EAAE,IAAI,EAAE,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE;IACpC,OAAO,EAAE,EAAE,IAAI,EAAE,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE;IAClC,QAAQ,EAAE,EAAE,IAAI,EAAE,GAAG,EAAE,KAAK,EAAE,GAAG,EAAE;IACnC,QAAQ,EAAE,EAAE,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE;CACvB,CAAC;AAEX,wGAAwG;AACxG,MAAM,QAAQ,GAAU,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,CAAC;AAE5D,uFAAuF;AACvF,MAAM,UAAU,IAAI,CAAC,IAAa,EAAE,IAAe,EAAE,OAAO,GAAgB,EAAE;IAC5E,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,EAAE,WAAW,IAAI,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC;IAClF,IAAI,OAAO,CAAC,KAAK,KAAK,SAAS,IAAI,IAAI,YAAY,WAAW;QAAE,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,WAAW,EAAE,OAAO,CAAC,KAAK,CAAC,CAAC;IACnH,IAAI,IAAI,GAAG,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC;IACzC,IAAI,GAAG,GAAG,IAAI,CAAC;IACf,IAAI,GAAG,GAAG,CAAC,CAAC;IACZ,IAAI,GAAG,GAA4B,IAAI,CAAC;IACxC,MAAM,GAAG,GAAG,IAAI,GAAG,EAAE,CAAC;IACtB,MAAM,KAAK,GAAG,OAAO,UAAU,KAAK,UAAU,IAAI,UAAU,CAAC,kCAAkC,CAAC,CAAC,OAAO,CAAC;IACzG,IAAI,KAAK,GAAU,EAAE,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;IACrC,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,IAAI,IAAI,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC;IAC7B,IAAI,QAAQ,GAAkB,IAAI,CAAC;IACnC,IAAI,OAAO,GAAG,SAAS,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC;IACvC,MAAM,IAAI,GAAG,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC;IAC5B,MAAM,MAAM,GAAG,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC;IAC9B,IAAI,QAAQ,GAAG,CAAC,CAAC;IACjB,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,IAAI,MAAM,GAAG,KAAK,CAAC;IAEnB,MAAM,IAAI,GAAG,CAAC,EAAU,EAAQ,EAAE;QAChC,MAAM,EAAE,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,IAAI,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC;QAC3D,IAAI,GAAG,EAAE,CAAC;QACV,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC;QACzB,GAAG,CAAC,IAAI,CAAC,GAAG,IAAI,KAAK,CAAC,KAAK,CAAC,CAAC;QAC7B,MAAM,KAAK,GAAG,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QAC7B,MAAM,IAAI,GAAG,KAAK,EAAE,KAAK,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QAC/E,kHAAkH;QAClH,IAAI,IAAI,YAAY,WAAW,EAAE,CAAC;YAChC,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,GAAG,CAAC,OAAO,CAAC;YACpC,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,GAAG,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;QAC5C,CAAC;QACD,MAAM,IAAI,GAAG,CAAC,KAAK,CAAC,KAAK,KAAK,MAAM,IAAI,GAAG,KAAK,IAAI,CAAC,IAAI,IAAI,GAAG,QAAQ,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;QACtG,MAAM,OAAO,GAAG,EAAE,GAAG,IAAI,CAAC;QAC1B,oEAAoE;QACpE,MAAM,GAAG,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,GAAG,IAAI,CAAC,EAAE,GAAG,CAAC,GAAG,IAAI,CAAC,CAAC;QAC/D,MAAM,MAAM,GACV,IAAI,KAAK,UAAU,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,EAAE,KAAK,EAAE,KAAK,IAAI,QAAQ,CAAC,CAAC,CAAC,CAAC,IAAI,KAAK,UAAU,CAAC,CAAC,CAAC,EAAE,GAAG,KAAK,CAAC,QAAQ,EAAE,KAAK,EAAE,GAAG,GAAG,GAAG,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QAC7I,KAAK,GAAG,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,CAAC,CAAC;QACtC,KAAK,GAAG,KAAK,CAAC,KAAK,EAAE,IAAI,KAAK,UAAU,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;QAErE,OAAO,IAAI,EAAE,CAAC;QACd,IAAI,QAAQ,KAAK,IAAI,IAAI,OAAO,IAAI,CAAC;YAAE,QAAQ,GAAG,CAAC,CAAC;QACpD,IAAI,GAAG,GAAG,CAAC,CAAC;QACZ,IAAI,QAAQ,KAAK,IAAI,EAAE,CAAC;YACtB,QAAQ,IAAI,EAAE,CAAC;YACf,GAAG,GAAG,KAAK,CAAC,QAAQ,CAAC,CAAC;YACtB,IAAI,QAAQ,IAAI,OAAO,EAAE,CAAC;gBACxB,QAAQ,GAAG,IAAI,CAAC;gBAChB,OAAO,GAAG,SAAS,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,CAAC;YACrC,CAAC;QACH,CAAC;QAED,kGAAkG;QAClG,qDAAqD;QACrD,QAAQ,IAAI,EAAE,CAAC;QACf,IAAI,IAAI,KAAK,UAAU,EAAE,CAAC;YACxB,MAAM,CAAC,CAAC,GAAG,GAAG,GAAG,GAAG,CAAC;YACrB,MAAM,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC;YAChB,QAAQ,GAAG,GAAG,CAAC;QACjB,CAAC;aAAM,IAAI,IAAI,KAAK,SAAS,EAAE,CAAC;YAC9B,MAAM,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC;YAChB,MAAM,CAAC,CAAC,GAAG,GAAG,CAAC;YACf,QAAQ,GAAG,GAAG,CAAC;QACjB,CAAC;aAAM,IAAI,QAAQ,IAAI,CAAC,EAAE,CAAC;YACzB,QAAQ,GAAG,GAAG,GAAG,IAAI,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;YACnC,MAAM,MAAM,GAAG,IAAI,KAAK,UAAU,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC;YAC/C,MAAM,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,GAAG,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC;YAC9C,MAAM,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,EAAE,GAAG,GAAG,CAAC,GAAG,MAAM,CAAC;QAC5C,CAAC;QACD,IAAI,CAAC,CAAC,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;QACjD,IAAI,CAAC,CAAC,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;QAEjD,MAAM,MAAM,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,GAAG,IAAI,CAAC,EAAE,GAAG,CAAC,GAAG,IAAI,CAAC,GAAG,KAAK,CAAC;QAC1E,GAAG,GAAG,OAAO,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE,CAAC,CAAC;QAC7B,GAAG,GAAG,KAAK,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;QAC3D,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,OAAO,GAAG,IAAI,CAAC,EAAE,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,KAAK,IAAI,GAAG,CAAC,CAAC,CAAC;QACxE,kGAAkG;QAClG,MAAM,GAAG,GAAG,GAAG,GAAG,IAAI,CAAC;QACvB,oFAAoF;QACpF,MAAM,IAAI,GAAG,EAAE,IAAI,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,EAAE,QAAQ,EAAE,CAAC,GAAG,GAAG,GAAG,GAAG,EAAE,QAAQ,EAAE,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC;QACjG,OAAO,CAAC,OAAO,EAAE;YACf,KAAK;YACL,GAAG;YACH,IAAI,EAAE,IAAI,KAAK,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,KAAK,WAAW,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YACjE,IAAI;YACJ,IAAI,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,OAAO,GAAG,GAAG,CAAC,GAAG,GAAG;YACtD,IAAI,EAAE,GAAG;YACT,KAAK,EAAE,EAAE,CAAC,EAAE,CAAC,GAAG,MAAM,GAAG,KAAK,GAAG,KAAK,GAAG,KAAK,CAAC,IAAI,GAAG,KAAK,GAAG,GAAG,GAAG,KAAK,EAAE,CAAC,EAAE,CAAC,GAAG,MAAM,GAAG,KAAK,GAAG,IAAI,GAAG,KAAK,CAAC,IAAI,GAAG,IAAI,GAAG,GAAG,GAAG,IAAI,EAAE;YAC1I,IAAI,EAAE,SAAS,CAAC,GAAG,CAAC;SACrB,CAAC,CAAC;QACH,KAAK,GAAG,qBAAqB,CAAC,IAAI,CAAC,CAAC;IACtC,CAAC,CAAC;IACF,IAAI,CAAC,WAAW,CAAC,GAAG,EAAE,CAAC,CAAC;IAExB,OAAO;QACL,IAAI;YACF,GAAG,CAAC,IAAI,EAAE,CAAC;QACb,CAAC;QACD,OAAO,CAAC,KAAK;YACX,IAAI,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC;QACtB,CAAC;QACD,MAAM,CAAC,OAAO;YACZ,GAAG,GAAG,OAAO,CAAC;QAChB,CAAC;QACD,KAAK;YACH,IAAI,MAAM;gBAAE,OAAO;YACnB,MAAM,GAAG,IAAI,CAAC;YACd,oBAAoB,CAAC,KAAK,CAAC,CAAC;YAC5B,GAAG,CAAC,KAAK,EAAE,CAAC;YACZ,OAAO,CAAC,GAAG,CAAC,MAAM,EAAE,CAAC;QACvB,CAAC;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/** How the face moves between frames: the mood a call is in, the mouth each quiet mood holds, the easing, the blink. Pure. */
|
|
2
|
+
import type { RoomState } from "@pinecall/room";
|
|
3
|
+
import type { Mouth } from "./mouth.js";
|
|
4
|
+
/** What the face is doing. Speaking is the voice's; the others are poses: listening waits in a call, hearing is the caller speaking. */
|
|
5
|
+
export type Mood = "idle" | "listening" | "hearing" | "thinking" | "speaking";
|
|
6
|
+
/** The mouth each quiet mood holds. */
|
|
7
|
+
export declare const POSES: Record<Exclude<Mood, "speaking">, Mouth>;
|
|
8
|
+
/** A frame louder than this is the agent speaking, whatever its state says: the log's word comes later than the sound. */
|
|
9
|
+
export declare const SOUNDING = 0.06;
|
|
10
|
+
/**
|
|
11
|
+
* The mood a call is in: opening thinks, a live call follows the agent's own state and hears while
|
|
12
|
+
* the caller speaks, anything else rests.
|
|
13
|
+
*/
|
|
14
|
+
export declare function moodOf(state: RoomState): Mood;
|
|
15
|
+
/** One number moved toward its target over `dt` seconds: `rise` seconds to open, `fall` to close. */
|
|
16
|
+
export declare function eased(now: number, target: number, dt: number, rise: number, fall: number): number;
|
|
17
|
+
/** The whole mouth moved toward its target: quick to open, slower to close, so it never shivers. */
|
|
18
|
+
export declare function easedMouth(now: Mouth, target: Mouth, dt: number): Mouth;
|
|
19
|
+
/** How long a blink takes, in seconds. */
|
|
20
|
+
export declare const BLINK_S = 0.16;
|
|
21
|
+
/** How open the eyes are `since` seconds into a blink: 1 open, down to 0.1, and back. */
|
|
22
|
+
export declare function lidAt(since: number): number;
|
|
23
|
+
/** Seconds to the next blink, from a random draw in [0, 1): now and then twice in a row, else every 2.5 to 6 s. */
|
|
24
|
+
export declare function nextBlink(draw: number): number;
|
|
25
|
+
//# sourceMappingURL=motion.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"motion.d.ts","sourceRoot":"","sources":["../../src/face/motion.ts"],"names":[],"mappings":"AAAA,8HAA8H;AAE9H,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAEhD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAExC,wIAAwI;AACxI,MAAM,MAAM,IAAI,GAAG,MAAM,GAAG,WAAW,GAAG,SAAS,GAAG,UAAU,GAAG,UAAU,CAAC;AAE9E,uCAAuC;AACvC,eAAO,MAAM,KAAK,EAAE,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE,UAAU,CAAC,EAAE,KAAK,CAK1D,CAAC;AAEF,0HAA0H;AAC1H,eAAO,MAAM,QAAQ,OAAO,CAAC;AAE7B;;;GAGG;AACH,wBAAgB,MAAM,CAAC,KAAK,EAAE,SAAS,GAAG,IAAI,CAW7C;AAED,qGAAqG;AACrG,wBAAgB,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAGjG;AAED,oGAAoG;AACpG,wBAAgB,UAAU,CAAC,GAAG,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE,EAAE,MAAM,GAAG,KAAK,CASvE;AAED,0CAA0C;AAC1C,eAAO,MAAM,OAAO,OAAO,CAAC;AAE5B,yFAAyF;AACzF,wBAAgB,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAI3C;AAED,mHAAmH;AACnH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE9C"}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/** How the face moves between frames: the mood a call is in, the mouth each quiet mood holds, the easing, the blink. Pure. */
|
|
2
|
+
/** The mouth each quiet mood holds. */
|
|
3
|
+
export const POSES = {
|
|
4
|
+
idle: { open: 0, width: 0.5, round: 0, teeth: 0, smile: 0.85, shift: 0 },
|
|
5
|
+
listening: { open: 0.04, width: 0.55, round: 0, teeth: 0, smile: 0.55, shift: 0 },
|
|
6
|
+
hearing: { open: 0.1, width: 0.36, round: 0.45, teeth: 0, smile: 0.3, shift: 0 },
|
|
7
|
+
thinking: { open: 0.14, width: 0.2, round: 0.75, teeth: 0, smile: 0, shift: 3.2 },
|
|
8
|
+
};
|
|
9
|
+
/** A frame louder than this is the agent speaking, whatever its state says: the log's word comes later than the sound. */
|
|
10
|
+
export const SOUNDING = 0.06;
|
|
11
|
+
/**
|
|
12
|
+
* The mood a call is in: opening thinks, a live call follows the agent's own state and hears while
|
|
13
|
+
* the caller speaks, anything else rests.
|
|
14
|
+
*/
|
|
15
|
+
export function moodOf(state) {
|
|
16
|
+
if (state.phase === "opening")
|
|
17
|
+
return "thinking";
|
|
18
|
+
if (state.phase !== "live")
|
|
19
|
+
return "idle";
|
|
20
|
+
switch (state.log.agent_state) {
|
|
21
|
+
case "speaking":
|
|
22
|
+
return "speaking";
|
|
23
|
+
case "thinking":
|
|
24
|
+
return "thinking";
|
|
25
|
+
default:
|
|
26
|
+
return state.speaking.user || (state.log.live.user ?? "") !== "" ? "hearing" : "listening";
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
/** One number moved toward its target over `dt` seconds: `rise` seconds to open, `fall` to close. */
|
|
30
|
+
export function eased(now, target, dt, rise, fall) {
|
|
31
|
+
const tau = target > now ? rise : fall;
|
|
32
|
+
return now + (target - now) * (1 - Math.exp(-dt / tau));
|
|
33
|
+
}
|
|
34
|
+
/** The whole mouth moved toward its target: quick to open, slower to close, so it never shivers. */
|
|
35
|
+
export function easedMouth(now, target, dt) {
|
|
36
|
+
return {
|
|
37
|
+
open: eased(now.open, target.open, dt, 0.035, 0.075),
|
|
38
|
+
width: eased(now.width, target.width, dt, 0.05, 0.09),
|
|
39
|
+
round: eased(now.round, target.round, dt, 0.05, 0.09),
|
|
40
|
+
teeth: eased(now.teeth, target.teeth, dt, 0.03, 0.07),
|
|
41
|
+
smile: eased(now.smile, target.smile, dt, 0.15, 0.15),
|
|
42
|
+
shift: eased(now.shift, target.shift, dt, 0.2, 0.2),
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
/** How long a blink takes, in seconds. */
|
|
46
|
+
export const BLINK_S = 0.16;
|
|
47
|
+
/** How open the eyes are `since` seconds into a blink: 1 open, down to 0.1, and back. */
|
|
48
|
+
export function lidAt(since) {
|
|
49
|
+
const p = since / BLINK_S;
|
|
50
|
+
if (p >= 1)
|
|
51
|
+
return 1;
|
|
52
|
+
return p < 0.5 ? 1 - p * 1.8 : Math.min(1, 0.1 + (p - 0.5) * 1.8);
|
|
53
|
+
}
|
|
54
|
+
/** Seconds to the next blink, from a random draw in [0, 1): now and then twice in a row, else every 2.5 to 6 s. */
|
|
55
|
+
export function nextBlink(draw) {
|
|
56
|
+
return draw < 0.18 ? 0.18 : 2.5 + ((draw - 0.18) / 0.82) * 3.5;
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=motion.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"motion.js","sourceRoot":"","sources":["../../src/face/motion.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/** The mouth, as numbers: what the voice makes of it, and its outline. Pure: no DOM, no audio. */
|
|
2
|
+
/** Every number 0 to 1, but `shift`, which moves the mouth sideways in the face's own units. */
|
|
3
|
+
export interface Mouth {
|
|
4
|
+
open: number;
|
|
5
|
+
width: number;
|
|
6
|
+
round: number;
|
|
7
|
+
teeth: number;
|
|
8
|
+
smile: number;
|
|
9
|
+
shift: number;
|
|
10
|
+
}
|
|
11
|
+
/** The voice's power in four bands, in any linear unit: only their proportions are read. */
|
|
12
|
+
export interface Bands {
|
|
13
|
+
low: number;
|
|
14
|
+
mid: number;
|
|
15
|
+
high: number;
|
|
16
|
+
sib: number;
|
|
17
|
+
}
|
|
18
|
+
/** Where the bands are cut, in Hz. */
|
|
19
|
+
export declare const BANDS: {
|
|
20
|
+
readonly low: readonly [90, 500];
|
|
21
|
+
readonly mid: readonly [500, 1500];
|
|
22
|
+
readonly high: readonly [1500, 3500];
|
|
23
|
+
readonly sib: readonly [4000, 9000];
|
|
24
|
+
};
|
|
25
|
+
export declare function clamp(value: number, low?: number, high?: number): number;
|
|
26
|
+
/** The loudest recent frame after `dt` seconds: a slow automatic gain, so any voice opens fully. */
|
|
27
|
+
export declare function peakAfter(peak: number, db: number, dt: number): number;
|
|
28
|
+
/** How loud a frame is, 0 (silence) to 1 (the loudest recent frame). */
|
|
29
|
+
export declare function levelOf(db: number, peak: number): number;
|
|
30
|
+
/** The mouth a frame of voice asks for: the jaw from `level`, the lips from where the energy is. */
|
|
31
|
+
export declare function spoken(level: number, bands: Bands): Mouth;
|
|
32
|
+
/** The mouth drawn in a 100 × 100 face: a lens between two cubic curves, and where its lips are. */
|
|
33
|
+
export interface Outline {
|
|
34
|
+
d: string;
|
|
35
|
+
cx: number;
|
|
36
|
+
cy: number;
|
|
37
|
+
/** Half its width. */
|
|
38
|
+
half: number;
|
|
39
|
+
/** How far the upper lip rises, and the lower one drops, from the line between the corners. */
|
|
40
|
+
up: number;
|
|
41
|
+
down: number;
|
|
42
|
+
}
|
|
43
|
+
/** Corners in for round, out for wide; the lower lip drops with `open`; the corners lift with `smile`. */
|
|
44
|
+
export declare function outline(mouth: Mouth): Outline;
|
|
45
|
+
//# sourceMappingURL=mouth.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mouth.d.ts","sourceRoot":"","sources":["../../src/face/mouth.ts"],"names":[],"mappings":"AAAA,kGAAkG;AAOlG,gGAAgG;AAChG,MAAM,WAAW,KAAK;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;CACf;AAED,4FAA4F;AAC5F,MAAM,WAAW,KAAK;IACpB,GAAG,EAAE,MAAM,CAAC;IACZ,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,GAAG,EAAE,MAAM,CAAC;CACb;AAED,sCAAsC;AACtC,eAAO,MAAM,KAAK;aAAK,GAAG,YAAG,EAAE,EAAE,GAAG;aAAG,GAAG,YAAG,GAAG,EAAE,IAAI;aAAG,IAAI,YAAG,IAAI,EAAE,IAAI;aAAG,GAAG,YAAG,IAAI,EAAE,IAAI;CAAY,CAAC;AAQ1G,wBAAgB,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,SAAI,EAAE,IAAI,SAAI,GAAG,MAAM,CAE9D;AAED,oGAAoG;AACpG,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,CAEtE;AAED,wEAAwE;AACxE,wBAAgB,OAAO,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,CAExD;AAED,oGAAoG;AACpG,wBAAgB,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,KAAK,GAAG,KAAK,CAkBzD;AAED,oGAAoG;AACpG,MAAM,WAAW,OAAO;IACtB,CAAC,EAAE,MAAM,CAAC;IACV,EAAE,EAAE,MAAM,CAAC;IACX,EAAE,EAAE,MAAM,CAAC;IACX,sBAAsB;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,+FAA+F;IAC/F,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;CACd;AAED,0GAA0G;AAC1G,wBAAgB,OAAO,CAAC,KAAK,EAAE,KAAK,GAAG,OAAO,CAe7C"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/** The mouth, as numbers: what the voice makes of it, and its outline. Pure: no DOM, no audio. */
|
|
2
|
+
//
|
|
3
|
+
// Loudness opens the jaw. Where the energy sits in the spectrum shapes the lips: strong highs over
|
|
4
|
+
// 4 kHz are S and T (teeth showing, little opening); strong 1.5–3.5 kHz is a front vowel, EE or EH
|
|
5
|
+
// (wide); energy held under 500 Hz with no highs is a back vowel, OO or O (round). Six numbers, not
|
|
6
|
+
// a set of drawings, so any face can be drawn from them.
|
|
7
|
+
/** Where the bands are cut, in Hz. */
|
|
8
|
+
export const BANDS = { low: [90, 500], mid: [500, 1500], high: [1500, 3500], sib: [4000, 9000] };
|
|
9
|
+
/** Below this the voice is silence, in dBFS. */
|
|
10
|
+
const FLOOR_DB = -52;
|
|
11
|
+
/** The loudest recent frame decays this fast, in dB a second, and never below this. */
|
|
12
|
+
const PEAK_DECAY = 4;
|
|
13
|
+
const PEAK_LOWEST = -30;
|
|
14
|
+
export function clamp(value, low = 0, high = 1) {
|
|
15
|
+
return Math.min(high, Math.max(low, value));
|
|
16
|
+
}
|
|
17
|
+
/** The loudest recent frame after `dt` seconds: a slow automatic gain, so any voice opens fully. */
|
|
18
|
+
export function peakAfter(peak, db, dt) {
|
|
19
|
+
return Math.max(db, peak - PEAK_DECAY * dt, PEAK_LOWEST);
|
|
20
|
+
}
|
|
21
|
+
/** How loud a frame is, 0 (silence) to 1 (the loudest recent frame). */
|
|
22
|
+
export function levelOf(db, peak) {
|
|
23
|
+
return clamp((db - FLOOR_DB) / (peak - FLOOR_DB));
|
|
24
|
+
}
|
|
25
|
+
/** The mouth a frame of voice asks for: the jaw from `level`, the lips from where the energy is. */
|
|
26
|
+
export function spoken(level, bands) {
|
|
27
|
+
const total = bands.low + bands.mid + bands.high + bands.sib + 1e-12;
|
|
28
|
+
const low = bands.low / total;
|
|
29
|
+
const mid = bands.mid / total;
|
|
30
|
+
const high = bands.high / total;
|
|
31
|
+
const sib = bands.sib / total;
|
|
32
|
+
const voiced = clamp((level - 0.06) * 4);
|
|
33
|
+
const hiss = clamp((sib - 0.06) / 0.22) * voiced;
|
|
34
|
+
const front = clamp((high - 0.06) / 0.22) * voiced;
|
|
35
|
+
const round = clamp((low - 0.6) / 0.3) * (1 - front) * (1 - hiss) * voiced;
|
|
36
|
+
return {
|
|
37
|
+
open: clamp(Math.pow(level, 0.85) * (0.6 + 0.7 * mid) * (1 - 0.6 * hiss) * (1 - 0.25 * round)),
|
|
38
|
+
width: clamp(0.35 + 0.65 * front - 0.3 * round),
|
|
39
|
+
round,
|
|
40
|
+
teeth: clamp(hiss * 1.2 + front * 0.45),
|
|
41
|
+
smile: 0.3,
|
|
42
|
+
shift: 0,
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
/** Corners in for round, out for wide; the lower lip drops with `open`; the corners lift with `smile`. */
|
|
46
|
+
export function outline(mouth) {
|
|
47
|
+
const cx = 50 + mouth.shift;
|
|
48
|
+
const cy = 64;
|
|
49
|
+
const half = 11 * (0.78 + 0.4 * mouth.width) * (1 - 0.45 * mouth.round);
|
|
50
|
+
const lift = mouth.smile * 3.4 * (1 - 0.75 * mouth.open);
|
|
51
|
+
const up = 0.5 + mouth.open * 4.2 + mouth.round * mouth.open * 2.2;
|
|
52
|
+
const down = 0.9 + mouth.open * 12.5;
|
|
53
|
+
const k = 0.42 + 0.38 * mouth.round;
|
|
54
|
+
const y = cy - lift;
|
|
55
|
+
const at = (n) => n.toFixed(2);
|
|
56
|
+
const d = `M ${at(cx - half)} ${at(y)}` +
|
|
57
|
+
` C ${at(cx - half * k)} ${at(cy - up)}, ${at(cx + half * k)} ${at(cy - up)}, ${at(cx + half)} ${at(y)}` +
|
|
58
|
+
` C ${at(cx + half * k)} ${at(cy + down)}, ${at(cx - half * k)} ${at(cy + down)}, ${at(cx - half)} ${at(y)} Z`;
|
|
59
|
+
return { d, cx, cy, half, up, down };
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=mouth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mouth.js","sourceRoot":"","sources":["../../src/face/mouth.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/** The face's body: one of a few shapes, as a radius at each of a ring of angles. Pure. */
|
|
2
|
+
/** The shapes a face's body takes. */
|
|
3
|
+
export declare const SHAPES: readonly ["circle", "squircle", "blob"];
|
|
4
|
+
export type Shape = (typeof SHAPES)[number];
|
|
5
|
+
/** The farthest a point may reach from the middle: the box's half, less what the head's bob and squash take (7 % and 1.5 units). */
|
|
6
|
+
export declare const REACH = 44;
|
|
7
|
+
/** `value` when it names a shape, else the circle. */
|
|
8
|
+
export declare function shapeOf(value: unknown): Shape;
|
|
9
|
+
/** The shape at rest: its radius at each angle of the ring. */
|
|
10
|
+
export declare function radii(shape: Shape): number[];
|
|
11
|
+
/** Each point moved toward the shape it is becoming over `dt` seconds, about a third of a second to arrive. */
|
|
12
|
+
export declare function morphed(now: readonly number[], target: readonly number[], dt: number): number[];
|
|
13
|
+
/** The rim as one smooth closed SVG path round (50, 50): a Catmull-Rom curve through its points. */
|
|
14
|
+
export declare function outlineOf(rim: readonly number[]): string;
|
|
15
|
+
//# sourceMappingURL=shape.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shape.d.ts","sourceRoot":"","sources":["../../src/face/shape.ts"],"names":[],"mappings":"AAAA,2FAA2F;AAO3F,sCAAsC;AACtC,eAAO,MAAM,MAAM,YAAI,QAAQ,EAAE,UAAU,EAAE,MAAM,CAAU,CAAC;AAC9D,MAAM,MAAM,KAAK,GAAG,CAAC,OAAO,MAAM,CAAC,CAAC,MAAM,CAAC,CAAC;AAI5C,oIAAoI;AACpI,eAAO,MAAM,KAAK,KAAK,CAAC;AAExB,sDAAsD;AACtD,wBAAgB,OAAO,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,CAE7C;AAID,+DAA+D;AAC/D,wBAAgB,KAAK,CAAC,KAAK,EAAE,KAAK,GAAG,MAAM,EAAE,CAW5C;AAED,+GAA+G;AAC/G,wBAAgB,OAAO,CAAC,GAAG,EAAE,SAAS,MAAM,EAAE,EAAE,MAAM,EAAE,SAAS,MAAM,EAAE,EAAE,EAAE,EAAE,MAAM,GAAG,MAAM,EAAE,CAG/F;AAED,oGAAoG;AACpG,wBAAgB,SAAS,CAAC,GAAG,EAAE,SAAS,MAAM,EAAE,GAAG,MAAM,CAYxD"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/** The face's body: one of a few shapes, as a radius at each of a ring of angles. Pure. */
|
|
2
|
+
//
|
|
3
|
+
// A shape is its radius at every angle around the middle, so any two shapes ease into each other
|
|
4
|
+
// point by point. The outline never bends while the face moves: the whole body bobs, leans and
|
|
5
|
+
// squashes instead. Every point stays inside the face's 100-unit box with room for that motion, so
|
|
6
|
+
// nothing of the body is drawn past the element the page gave it.
|
|
7
|
+
/** The shapes a face's body takes. */
|
|
8
|
+
export const SHAPES = ["circle", "squircle", "blob"];
|
|
9
|
+
/** How many points go round the rim. */
|
|
10
|
+
const POINTS = 48;
|
|
11
|
+
/** The farthest a point may reach from the middle: the box's half, less what the head's bob and squash take (7 % and 1.5 units). */
|
|
12
|
+
export const REACH = 44;
|
|
13
|
+
/** `value` when it names a shape, else the circle. */
|
|
14
|
+
export function shapeOf(value) {
|
|
15
|
+
return SHAPES.find((one) => one === value) ?? "circle";
|
|
16
|
+
}
|
|
17
|
+
const angles = Array.from({ length: POINTS }, (_, at) => (at / POINTS) * Math.PI * 2);
|
|
18
|
+
/** The shape at rest: its radius at each angle of the ring. */
|
|
19
|
+
export function radii(shape) {
|
|
20
|
+
switch (shape) {
|
|
21
|
+
case "circle":
|
|
22
|
+
return angles.map(() => 44);
|
|
23
|
+
case "squircle":
|
|
24
|
+
// A superellipse, flat on its four sides and round at its corners, which reach farther: 36 is 43.6 there.
|
|
25
|
+
return angles.map((a) => 36 / (Math.abs(Math.cos(a)) ** 4.5 + Math.abs(Math.sin(a)) ** 4.5) ** (1 / 4.5));
|
|
26
|
+
case "blob":
|
|
27
|
+
// A soft egg leaning a little to one side: one gentle swell, nothing a viewer reads as a dent.
|
|
28
|
+
return angles.map((a) => 41 * (1 + 0.055 * Math.sin(2 * a + 0.6) + 0.015 * Math.sin(3 * a + 1.9)));
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
/** Each point moved toward the shape it is becoming over `dt` seconds, about a third of a second to arrive. */
|
|
32
|
+
export function morphed(now, target, dt) {
|
|
33
|
+
const step = 1 - Math.exp(-dt / 0.12);
|
|
34
|
+
return now.map((radius, at) => radius + ((target[at] ?? radius) - radius) * step);
|
|
35
|
+
}
|
|
36
|
+
/** The rim as one smooth closed SVG path round (50, 50): a Catmull-Rom curve through its points. */
|
|
37
|
+
export function outlineOf(rim) {
|
|
38
|
+
const points = rim.map((radius, at) => ({ x: 50 + radius * Math.cos(angles[at] ?? 0), y: 50 + radius * Math.sin(angles[at] ?? 0) }));
|
|
39
|
+
const at = (index) => points[(index + points.length) % points.length] ?? { x: 50, y: 50 };
|
|
40
|
+
const n = (value) => value.toFixed(2);
|
|
41
|
+
let d = `M ${n(at(0).x)} ${n(at(0).y)}`;
|
|
42
|
+
for (let index = 0; index < points.length; index++) {
|
|
43
|
+
const [before, from, to, after] = [at(index - 1), at(index), at(index + 1), at(index + 2)];
|
|
44
|
+
const c1 = { x: from.x + (to.x - before.x) / 6, y: from.y + (to.y - before.y) / 6 };
|
|
45
|
+
const c2 = { x: to.x - (after.x - from.x) / 6, y: to.y - (after.y - from.y) / 6 };
|
|
46
|
+
d += ` C ${n(c1.x)} ${n(c1.y)}, ${n(c2.x)} ${n(c2.y)}, ${n(to.x)} ${n(to.y)}`;
|
|
47
|
+
}
|
|
48
|
+
return `${d} Z`;
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=shape.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"shape.js","sourceRoot":"","sources":["../../src/face/shape.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/** @pinecall/dot: the talking dot for a page — its face, and the tutor that walks the page with a pointer of its own. */
|
|
2
|
+
export { face, type Face, type FaceOptions } from "./face/index.js";
|
|
3
|
+
export type { Shape } from "./face/shape.js";
|
|
4
|
+
export { Page, type Seen } from "./page/page.js";
|
|
5
|
+
export { tutor, type Tutor, type TutorOptions } from "./tutor/tutor.js";
|
|
6
|
+
export type { ViewRenderer, ViewTools, Views } from "./tutor/views.js";
|
|
7
|
+
export type { Action, Beat, Cursor, Macro, MacroStep, Step } from "./wire/index.js";
|
|
8
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,yHAAyH;AAEzH,OAAO,EAAE,IAAI,EAAE,KAAK,IAAI,EAAE,KAAK,WAAW,EAAE,MAAM,iBAAiB,CAAC;AACpE,YAAY,EAAE,KAAK,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,IAAI,EAAE,KAAK,IAAI,EAAE,MAAM,gBAAgB,CAAC;AACjD,OAAO,EAAE,KAAK,EAAE,KAAK,KAAK,EAAE,KAAK,YAAY,EAAE,MAAM,kBAAkB,CAAC;AACxE,YAAY,EAAE,YAAY,EAAE,SAAS,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AACvE,YAAY,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/** @pinecall/dot: the talking dot for a page — its face, and the tutor that walks the page with a pointer of its own. */
|
|
2
|
+
export { face } from "./face/index.js";
|
|
3
|
+
export { Page } from "./page/page.js";
|
|
4
|
+
export { tutor } from "./tutor/tutor.js";
|
|
5
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,yHAAyH;AAEzH,OAAO,EAAE,IAAI,EAA+B,MAAM,iBAAiB,CAAC;AAEpE,OAAO,EAAE,IAAI,EAAa,MAAM,gBAAgB,CAAC;AACjD,OAAO,EAAE,KAAK,EAAiC,MAAM,kBAAkB,CAAC"}
|