File size: 5,486 Bytes
8aa3e04
 
073ffab
8aa3e04
 
d51d114
 
 
 
 
 
 
 
073ffab
d51d114
073ffab
d51d114
8aa3e04
 
073ffab
 
d8622a2
 
 
073ffab
 
 
 
 
 
 
 
 
c6af819
 
 
 
d8622a2
 
 
073ffab
d51d114
 
 
89686a6
 
 
 
 
 
d51d114
89686a6
 
 
 
 
 
d51d114
073ffab
 
 
 
 
 
 
 
 
 
d51d114
d8622a2
 
073ffab
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
d8622a2
 
8aa3e04
 
 
073ffab
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
# Architecture

## One engine, many hosts

```
   crates/microquack-core        crates/microquack-llm
   (canonical: all sound          (the droid mind: tiny
    behavior)                      transformer inference)
        \                          /            |
         crates/microquack-wasm            trained in llm/
        /      (browser binding)           (PyTorch -> binary weights)
  crates/microquack-cli
  (render / validate / fit / schema)
           |
      apps/web (TypeScript: UI, canvas, WebAudio, 3D stage only)
           |
  microduck robot (native crates, 48 kHz PCM)   <- planned consumer
```

`microquack-core` contains everything that affects the sound: score types and
parsing, pitch curves, envelopes, oscillators and partials, ring modulation,
noise, the seeded PRNG, phrase grammar, the canonical semantic emotion
registry and voice-mode shaping, contour output for motion, and rendering β€”
both full-phrase (`render_score`) and incremental
blocks (`Engine::render_into`, which `render_score` itself uses, so offline
and streaming output are identical). It contains no WebAudio, WASM APIs,
device access, filesystem loading, browser code, or Python-specific code.

## The score contract

A score is a JSON list of syllables with snake_case fields (`f0_start`,
`amp_points`, ...), defined once as Serde types in `microquack-core`.
`schema/score.schema.json` is generated from those types (`pnpm run schema`);
nothing else defines the format. `schema/emotions.json` and
`apps/web/src/emotions.generated.ts` are likewise generated from the Rust
registry with `pnpm run emotions`. Python uses the JSON file for teacher-data
generation. Scores use seconds and hertz, so they are sample-rate
independent; the renderer owns all rate-specific behavior. The TypeScript app
treats scores as opaque JSON strings β€” it never reimplements or redeclares the
format.

## The chat and the droid mind

The visitor reads and writes English; the robot answers in droid speak with
an English gloss. `crates/microquack-llm` runs a ~1.3M-parameter
character-level transformer (trained from scratch in `llm/` on a synthetic
space-adventure corpus) with a KV cache and greedy decoding, compiled into
the same WASM module as the synth. Its output is `(emotion, reply)`; the
emotion drives phrase generation and the body-language gestures. Rust/
PyTorch parity is enforced by `cargo test -p microquack-llm` against the
committed model (`apps/web/public/assets/llm/droidmind.bin`) and test
vectors. The current model artifact supports its original eight emotions.
Rust validates that list against the canonical 73-state registry, then maps
each semantic state to one of 12 broad voice modes and four shared affect
values. The expanded 1,000-row teacher set is review data only; it does not
replace the production weights. Between conversations the robot utters an
idle thought every 10–15 seconds.

## The web app

`apps/web` owns UI, canvas, and WebAudio only:

- `src/wasm/` β€” generated wasm-bindgen output (`pnpm run build:wasm`).
- `robot.ts` β€” assembles the real microduck from its mesh library
  (`microduck.glb`) and kinematic tree (`kinematics.json`, MuJoCo
  convention), exposes head joints and a synthetic jaw hinge.
- `scene.ts` β€” the procedural night-plain world (three moons, stars, rocks)
  and the speech-driven articulation.
- `mind.ts` / `droidtext.ts` / `gestures.ts` β€” the chat brain wrapper,
  droid-speak captions, and generic body language driven by the Rust voice
  mode and affect values. TypeScript does not map the 73 semantic states.
- Short phrases render whole (`render_score` β†’ `AudioBuffer`). Continuous
  synthesis, when it arrives, must run the `Engine` in an AudioWorklet β€”
  WASM alone does not prevent main-thread stalls.

## Research

Fitting is a CLI subcommand (`microquack fit`): it analyzes a real WAV
(pitch track, envelope, harmonics, noisiness) and emits a breakpoint score,
rendering every candidate through the same engine that ships. `research/`
holds the fitted corpus (`scores/fits.json`), pregenerated phrase scores,
and the frozen conformance fixtures.

## Conformance gate

`crates/microquack-core/tests/conformance.rs` renders all 90 frozen scores
(79 fitted + 6 phrases + 5 presets) against `research/fixtures/`: exact
sample counts, RMS/peak within 1e-6, and 21 full waveforms within 1e-4 per
sample. Streaming must equal offline rendering exactly, and phrase generation
must be seed-deterministic. Any DSP change that breaks these is a behavior
change and requires refreezing fixtures deliberately.

## Device integration (planned)

The Microduck repository consumes `microquack-core` at an exact version
(`microquack-core = "=x.y.z"`, or a pinned git revision until published) β€”
never vendored source. The device adapter feeds `Engine::render_into` into
its existing 48 kHz PCM path and reads `ControlFrame` values to animate the
physical beak and body. The 79 fitted scores must be revalidated at 48 kHz
before the robot ships them.

## Release model

One git tag should produce, all reporting the same version and commit:

- the `microquack-core` crate (device),
- the WASM/npm package (website),
- the CLI binary (render, validate, fit, schema, emotions),
- the score corpus and generated schemas.

## Roadmap

See [VISION.md](VISION.md): preset conversations, the droid-speak language
model, procedural full-body motion, and browser-side locomotion policies from
`microduck_rl` via ONNX Runtime Web.