AMENTI.LIVE

THE VOICE THAT CARRIES

How a library learned to draw breath, speak in measures,
and answer back in its own voice.
recitation layer :: measures cached :: status: CARRYING

Prologue

The Silent Library

Amenti began as a library you could read but not hear. A thousand figures — Isis, Ian Ingram, the named and the half-remembered — each with a Reading Room and primary-source documents on the page. The words were there. The voice was not.

This is the chronicle of how that voice arrived: not as a single feature bolted on, but as a set of faculties assembled one at a time, until the library could draw breath, speak in measures, remember what it had said, and answer back in a voice that belonged to the figure speaking. We came to call the whole of it the Amenti Neural Interface — less a pipeline than a mind, with parts that think, articulate, remember, carry, and become.

It is also, underneath, the story of a machine learning to meter its own fuel — which is where we begin.


Chapter I

The Dilemma — A Breath Held Too Long

The first attempt at speech was the obvious one: take the whole document, hand it to the synthesizer in a single call, wait for the audio, play it. For short passages it worked. For long ones it died — every time — with the same blunt error: HTTP 524. A timeout. The connection, held open at the edge for the better part of two minutes, was severed before a single second of audio returned.

This is the kind of bug that is dangerous precisely because the easy reading of it is wrong. It looks like a performance problem — something to be solved by a faster model or a higher timeout. It is neither. It is an architectural problem about how a long utterance should be produced at all. No human orator speaks a thousand words without pausing for air. Why should a synthetic one?

There is a cleaner way to see it, and it will run quietly through the rest of this chronicle. An engine does not run on a tank of fuel dumped into the cylinder all at once — do that, and it floods: drowned, unable to fire, dead at the line. The single monolithic render is exactly that flood. The entire document, the whole charge, forced through one combustion event the timing cannot accommodate. The cure is not more fuel or a bigger cylinder. It is metering — admitting the charge in measured amounts, at the precise moment the engine is ready to burn it.

THE RESOLUTION — SPEAK IN MEASURES The thought is divided into phrasings; each is its own fast call; the voice plays gap-free as it arrives. The full thought (a long document) phrase measure 1 measure 2 measure 3 measure n… fast /speak calls render · cache render · cache render · cache Web Audio clock queue · schedule gap-free playback PLAYBACK — continuous, begins almost at once measure 1 measure 2 measure 3 measure 4 measure 5 ▲ first measure sounds in seconds — the rest generate behind it No single call ever approaches the ceiling — the breath is never held too long.
Figure 1 — The dilemma. One monolithic request for a long body of text exceeds the edge timeout; the connection is severed before any audio is heard.

Chapter II

The Resolution — Speaking in Measures

The fix follows from the diagnosis. If the whole breath cannot be held, divide it. The document is split into measures — runs of one or more sentences, packed up to a fixed size — and each measure becomes its own fast request to the synthesizer. The first measure returns in seconds, not minutes; playback begins almost immediately while later measures are still being rendered.

function chunkText(text, maxChars) {
  var paragraphs = String(text).split(/\n{2,}/);   // keep paragraph breaks
  var measures = [];
  for (var p = 0; p < paragraphs.length; p++) {
    var para = paragraphs[p].replace(/\n/g, " ").trim();
    if (!para) continue;
    var sentences = splitSentences(para), cur = "";
    for (var i = 0; i < sentences.length; i++) {
      var s = sentences[i];
      if (cur && cur.length + 1 + s.length > maxChars) {
        measures.push(cur); cur = s;
      }
      else cur = cur ? cur + " " + s : s;     // pack short sentences
    }
    if (cur) measures.push(cur);
  }
  return measures;       // deterministic: same text, same split
}
chunkText — divide the breath into measures, deterministically

The division must be deterministic: the same text always splits the same way. This is not fastidiousness — it is what lets memory work, as the next chapter shows. Returned audio is scheduled back-to-back on the Web Audio clock, each measure beginning exactly where the last one ended, so the listener hears one continuous voice rather than a series of clips.

function scheduleBuf(buf, rest, rate) {
  var src = ctx.createBufferSource();
  src.buffer = buf;
  src.connect(ctx.destination);
  var at = Math.max(player.nextStart, ctx.currentTime + 0.05);
  src.start(at);                       // fire at the seam, gap-free
  player.nextStart = at + buf.duration / (rate || 1) + (rest || 0);
}
scheduleBuf — each measure fired at the seam, gap-free
THE NEURAL INTERFACE — FACULTIES OF A SPEAKING MIND Each faculty was built in turn. The mind forms the thought, then gives it breath, then voice. 1 · COGNITION Claude reasons in-figure — the thought is formed in each persona's own mind. thinks 2 · ARTICULATION Gemini gives the thought a voice — composed from each figure's own record, directed not cloned. speaks 3 · MEMORY A content-addressed vault — render once, recall forever; the spoken word is kept, not re-conjured. remembers 4 · BREATH The long voice carries — speech divided into measures, played gap-free; cadence and rest at the seams. carries 5 · EMBODIMENT Each figure speaks as itself — gender, dialect, and character resolved from the roster, by name. becomes "The mind forms the thought, then gives it voice." — the organizing principle, repeated at a larger scale.
Figure 2 — The resolution. The thought is divided into measures; each is a fast, independent synthesis call; the Web Audio clock schedules the returned audio back-to-back so playback is continuous and begins within seconds.

This is fuel injection, not a carburetor. The text is the fuel — already in the tank, already written; nothing here generates words on demand. The phrasing rule is the throttle plate, deciding how much is admitted per stroke. Each measure is an injector firing its metered charge. And the Web Audio clock is the engine's timing — the structure that says now, and now, so each charge fires exactly where the last one finished its burn. Nothing is forced through the line; everything is delivered at the moment it is needed. An engine that was flooding now idles, revs, and pulls cleanly.


Chapter III

Memory — Render Once, Recall Forever

Synthesis is expensive; silence is cheap. The third faculty is memory: every measure of audio, once produced, is stored so it never has to be produced again. The store is content-addressed — the key is a hash of everything that determines the sound: the model, the voice, the style, and the exact text of the measure.

// AMENTI :: AI PROXY — POST /speak   (Gemini TTS -> WAV)
if (url.pathname === "/speak" && request.method === "POST") {
  const { text, style, voice } = await request.json();

  // render-once, recall-forever: content-address every clip in R2
  const audioKey = "tts:" + await sha256Hex(
    TTS_MODEL + voice + style + text
  );
  const cached = env.AUDIO && await env.AUDIO.get(audioKey);
  if (cached) return new Response(cached.body, { cache: "hit" });

  // each measure is small by design — it clears the edge timeout
  const gem = await fetch(TTS_ENDPOINT, makeRequest(text, voice));
  const wav = pcmToWav(decodeAudio(gem), 24000);
  if (env.AUDIO) await env.AUDIO.put(audioKey, wav);   // keep the breath
  return new Response(wav, { type: "audio/wav" });
}
the /speak route — render once, then recall from the content-addressed store

Because the same text always divides into the same measures, the same measures always hash to the same keys, and the second reading of any document is drawn from memory in milliseconds. This is why the chunker's determinism mattered: change how the text is divided and every stored key is orphaned — the library forgets its own voice. Hold it steady and the library remembers everything it has ever said.

Render once, recall forever. The deterministic measure is not a convenience; it is the thing that makes memory possible at all.

Chapter IV

The Faculties — A Mind, Assembled

Step back and the parts arrange themselves into something that behaves less like a pipeline and more like a mind. Each stage is a faculty, and each faculty does one thing the others cannot.

CADENCE & EMBODIMENT Rhythm from the text · Voice from the record BREATH — the text writes its own rhythm Punctuation chooses the rest at each seam. No tuning — the score is already on the page. …clause, soft a full sentence. beat closing the paragraph. long pause New thought… comma → short rest · . ! ? → one beat · paragraph end → long measure EMBODIMENT — voice resolved from the roster The room knows the figure's name · the roster knows the voice · joined by name, not slug Catalog (room) key: "ingram" name: "Ian Ingram" works: [ … ] ◄ join key lower-cased Roster (the record) "ian ingram" ◄ matched Gender · Dialect · Voice male · American English · bright composed Composed voice voice → Charon register + dialect + character + pace If the figure is not found: falls back to a neutral voice — it never breaks, it only declines to embody. Ian → Charon, "expressive and bright." Isis → Kore, "tender and lyrical." One rule, every figure. The join in code const voice = roster[ figureName.toLowerCase() ]; // "Ian Ingram" → "ian ingram" → row → { voice:"Charon", dialect:"American English" } // "" (empty) → default voice (Kore) — no match never crashes the engine // The catalog slug ("ingram") is irrelevant — the join is always on the display name
Figure 3 — Cadence and embodiment. Punctuation sets the rhythm; the roster resolves each figure's voice by name. One rule, every figure — it never breaks, it only declines to embody.

Cognition divides the thought into measures. Articulation gives each measure sound. Memory keeps what has been spoken. Breath carries the measures on the clock, with rests between them. And embodiment — the faculty the next chapter is about — makes the voice a particular voice, not a generic one. None of these is intelligent alone. Assembled, they read.


Chapter V

Embodiment — Each Figure, Its Own Voice

A library of a thousand figures should not speak in one voice. The embodiment faculty resolves, for each figure, the voice that belongs to them — drawn generically from the same roster that every Amenti surface shares, so nothing is hand-coded per person.

function resolveVoice(name) {
  return loadRoster().then(function (map) {
    var fig = map[String(name).toLowerCase().trim()];   // join on NAME, not slug
    return {
      voice: baseVoiceFor(fig && fig.gender),   // male -> Charon, female -> Kore
      style: composeStyle(fig)                  // register + dialect + character
    };
  });
}
resolveVoice — look the figure up by name, derive voice and style

The single decision that made this work was the join. The roster is keyed by a figure's full name, not by the catalog's short slug — so the lookup matches on name, lower-cased, and falls back to a neutral voice when a figure is absent rather than failing. Wired in at the moment a Reading Room opens, it means Ian Ingram reads in one voice and Isis in another, each carrying its own register, dialect, and character, with no per-figure code at all.

The library stopped narrating its figures and started letting them speak. Embodiment is the difference between a voice and a particular voice.

Chapter VI

Cadence — The Hand on the Throttle

Two refinements separate a voice that reads correctly from one that reads well: rhythm and pace.

Rhythm — the rests between measures

Speech is shaped as much by its silences as its sounds. Rather than play measures edge to edge, the scheduler inserts a rest at each seam, chosen by the punctuation the measure ends on — a short rest after a comma, a longer one after a sentence, a full measure of silence at the end of a paragraph. The text's own punctuation becomes its score.

function restFor(measure, endsParagraph) {
  if (endsParagraph) return REST_PARA;            // 0.85s — a long measure of silence
  var last = measure.slice(-1);
  if (last === "." || last === "!" || last === "?") return REST_SENTENCE;  // 0.38s
  if (last === "," || last === ";" || last === ":") return REST_SOFT;      // 0.16s
  return REST_SENTENCE;
}
restFor — the punctuation chooses the silence

Pace — and the chipmunk that taught us

The first attempt at speed was naïve: speed up playback. It worked, and it ruined everything — raising the playback rate raised the pitch with it, and every figure spoke like a chipmunk. The lesson was sharp and worth keeping: a faster chipmunk is worse than a true voice at a calm pace. We reverted the resampling entirely and instead asked the model for pace, in words, as part of the style.

var PACE_DIRECTION =
  "Speak at a brisk, lively, natural pace, as a person " +
  "speaking energetically — not slow or ponderous";
// asked of the model, not of the sample rate — so no chipmunk
pace, asked of the model — not of the sample rate

Seen through the metaphor, cadence and pace are the hand on the throttle. The body of a paragraph is the open road — brisk, the charge admitted freely. The cadence points are the corners — ease off, let the rests breathe, settle the thought before accelerating into the next. The system no longer takes whatever is forced through the line at a single fixed rate; it meters delivery to the shape of the road the text lays down. That is the whole of it: fuel injected at the precise moment it is needed, within a metered structure, sped up and slowed at will.

CADENCE & EMBODIMENT — RHYTHM FROM THE TEXT, VOICE FROM THE RECORD BREATH · the text writes its own rhythm Punctuation chooses the rest at each seam. No tuning — the score is already on the page. …clause, soft a full sentence. beat closing the paragraph. measure (long) New thought… comma → short rest · . ! ? → one beat · paragraph end → a long measure EMBODIMENT · voice resolved from the roster The room knows the figure's name; the roster knows the figure's voice. Joined by name, not slug. Catalog (room) key: "ingram" name: "Ian Ingram" ◄ join key works: [ … ] lower-cased Roster (the record) "ian ingram" ◄ matched row Gender · Dialect · Voice male · American English · bright Composed voice voice → Charon (male) style → register + dialect + character + brisk pace If the figure is not found, the reading falls back to a neutral voice — it never breaks, it only declines to embody. Ian → Charon, "expressive and bright." Isis → Kore, "tender and lyrical." One rule, every figure.
Figure 4 — Cadence and embodiment. Rests are chosen by punctuation and inserted at the seams; pace is requested of the model in words; each figure's voice is resolved from the shared roster.

Chapter VII

Full Ascension — The Voice That Carries

Assembled, the faculties do something none of them could alone: the library reads, in measures, in memory, in each figure's own voice, at a human pace, and it carries.

A reader opens a Reading Room and presses a single control. Behind it: the thought is divided into measures; each measure is rendered fast or recalled instantly from memory; the measures are carried on the clock with rests chosen by their punctuation; and the voice is the figure's own. What began as a held breath that ended in a timeout is now a voice that draws breath and keeps speaking for as long as there are words.

The failure that started all of this — the flooded engine, the whole charge forced through at once — is the thing the architecture now makes impossible. Fuel is metered. The engine pulls. The voice carries.

From a timeout to a living voice. The library learned to breathe, and having learned, it does not forget.

Reference

Glossary

Measure
A run of one or more sentences, packed up to a fixed size, synthesized as a single unit. The breath the library speaks in. (In the source the variable is still called chunk; in the design it is a measure.)
The throttle / fuel injection
The mechanical metaphor for metered delivery that runs through this chronicle. The words are fuel already in the tank; the phrasing rule is the throttle plate; each measure is an injector firing a metered charge; the Web Audio clock is the engine timing. The original failure — the whole charge forced through at once — is a flooded engine: the 524 made mechanical. Fuel injection, not a carburetor: discrete charges on a schedule, not a continuous pour.
Content-addressed memory
Audio stored under a key derived from the model, voice, style, and exact text — so identical measures are produced once and recalled forever.
Embodiment
Resolving each figure's own voice and style generically from the shared roster, joined on the figure's name. The difference between a voice and a particular voice.
Cadence
Rhythm and pace together: rests chosen by punctuation and inserted at the seams, and pace requested of the model in words rather than imposed by resampling.
524
A Cloudflare edge timeout. Here, the signature of a breath held too long — the flooded engine — and the failure the architecture was built to make impossible.

Appendix · Engineering Handoff

Terminal Velocity

Everything above is the why. This is the how — written to be read cold by whoever maintains the recitation layer next. The engine is built; this is the manual for keeping it running and bolting new surfaces onto it.

The one thing to understand first

There is one recitation engine — amenti-throttle.js, the throttle — and every long-form read-aloud surface calls it. It earns its place twice over: it defeats the 524 by splitting text into measures and streaming them gap-free on the Web Audio clock, and it makes the R2 cache shared across surfaces. That second property is conditional. The Worker keys each measure on sha256(model + voice + style + measure-text), so two surfaces share cached audio only if they chunk, strip, and compose the style string identically. The chunker, the markdown strip, the cadence constants, and composeStyle are therefore locked — byte-identical to the deployed library.js. Change any of them and every surface's cache silently forks.

The files

FileRole
amenti-throttle.jsThe shared engine. Exposes window.Amenti.throttle. Ship it next to any page that uses it.
library.jsThe reading-room renderer and the parity reference — the throttle was lifted from it verbatim.
Page1.htmlHosts Atlantica, Daily Planet, the Terminal, and the Council Chambers. Includes library.js then amenti-throttle.js.
voiceprofiles.jsDefines window.AMENTI_VOICE — the separate short-reply speaker. Not the throttle.
Include order matters. library.js before amenti-throttle.js, both before any inline script that calls the throttle. In Page1.html this is already correct.

Who speaks how — and why

SurfaceEngineVoiceCached
Reading roomthrottleembodiedyes (shared)
Atlanticathrottleembodiedyes (shared)
Daily Planetthrottleembodiedyes (shared)
Council Chambersthrottleper-card legend voices; synthesis in Amenti'syes (shared)
TerminalAMENTI_VOICEgenericno — deliberate

The Terminal stays off the throttle on purpose. Its replies are live chat — short, unique per turn, ephemeral. Too short to time out, never read twice, so chunking and caching would only add latency. The throttle solves problems the Terminal does not have. (If embodiment is ever wanted there, the right move is to give AMENTI_VOICE.speak the same roster lookup, keeping it a single short clip — not to route it through the throttle.)

The throttle's public API

window.Amenti.throttle = {
  speak(text, btn, figureName, onDone),  // chunk + cadence + embody + play; re-call on same btn = stop
  stop(),                                // cancel everything
  isReading(),                           // true while a session is active
  attach(btn, { text, figure, label }),  // wire an existing button (text/figure may be functions)
  chunk(text), plainText(md), resolveVoice(figureName), CHUNK_MAX
}
the surface a new caller talks to

The locked constants

VOICE_REGISTER     = 'Read clearly, in a measured, dignified tone'
VOICE_NAME_DEFAULT = 'Kore'          // male -> Charon, female -> Kore
CHUNK_MAX = 320      CHUNK_LOOKAHEAD = 2
CHUNK_TIMEOUT = 60000   START_TIMEOUT = 40000
RATE_FAST = RATE_SLOW = 1.0          // no resampling -- avoids the chipmunk
REST_SOFT = 0.16   REST_SENTENCE = 0.38   REST_PARA = 0.85
PACE_DIRECTION = 'Speak at a brisk, lively, natural pace, ...'
// style string: "<register>. Accent and dialect: <dialect>. Voice character: <voice>. <pace>"
must stay byte-identical to library.js
Historical trap: an earlier Atlantica build used 'Accent: ...' (no “and dialect”) and 700/1100-char chunks. That forked its cache — both were replaced. If you ever touch the style string or chunk size, you have re-broken this.

Deploy

Drop amenti-throttle.js, library.js, and Page1.html in the same folder (relative-path includes). One-time cache re-pay: the first read of each piece after deploy renders once at the unified measure boundaries, then caches forever. The button-position changes re-pay nothing — pure layout.

Wiring a new surface

// fixed body, figure's voice:
btn.addEventListener('click', function () {
  Amenti.throttle.speak(body, btn, figureName);   // '' for default voice
});
// multi-part, multi-voice (the Council pattern):
Amenti.throttle.speak(text, btn, figure, next);    // onDone advances the queue
do not re-implement chunking/strip/style in the surface — that is what forks the cache

Verify parity after ANY edit to library.js or the throttle

Run node verify_parity.js in the throttle dir. It extracts the chunker, strip, and composeStyle from the deployed library.js and asserts identical measures and style strings against the shared module. Expect: FULL PARITY — cache keys will align across surfaces. A mismatch means the cache has forked; stop and reconcile before shipping.

Open items

Fuel metered, engine pulling, cache aligned. The voice carries — and now it carries on every surface that asks it to.
Ingram Manor LLC · ©2026 All rights reserved