# Character Forge

Characters, built with code. A small procedural JavaScript character and collectible-card studio by Mitchell Argamasilla.

## Why

The project began as an experiment in consistent character artwork. Saved appearance settings make characters repeatable, while improving shared drawing functions can improve an entire collection. Ancient fantasy and modern people share facial, hair, expression, and card-rendering logic; environments and outfits vary by setting.

## Run

Open `index.html` in a modern browser. The core demo has no dependencies, external artwork, account system, or API calls. The Download Source link saves the main HTML file. Keep `comparison-v0.4.html` beside it for the optional version comparison; that companion preserves the actual v0.4 renderer in an isolated frame. `v0.4.html` remains the untouched original snapshot.

Select a collection and a character, change the appearance controls, or generate six or twelve characters from a numeric seed. Export PNG cards or JSON records. Import JSON using the studio's Import control. Switching collections retains edits in memory, but reloading does not: export your JSON to keep your work.

## How It Works

- `rng` provides deterministic seeded variation.
- `generateBatch` creates ancient character records; `modernBatch` adapts those records for contemporary settings.
- `portraitFace`, `portraitHair`, and the shared hair helpers draw portraits.
- `portraitBody` selects ancient garments or `modernBody`.
- `scene` and `modernScene` draw collection-specific environments.
- `art` assembles the illustration; `render` draws the card and exact labels.
- `PRESETS` and `MODERN_PRESETS` supply the example collections.

The current engine stays in one file; its optional historical comparison uses a separate snapshot rather than mixing two generations of drawing functions.

## v0.8

Modern 2026 uses a contemporary sports-card design via `modernCard`. Ancient characters retain their ornate card design. Both layouts call `art`, which calls the same `portraitFace` and hair functions. A focused test instruments that facial function and verifies both collections invoke it.

Facial settings now vary jaw/chin proportions, eye spacing, nose width/length, and expression-dependent brows. The comparison shows an ancient character with identical saved data in the actual v0.4 renderer and v0.8. Modern characters fall back to the first ancient character for this comparison, since v0.4 did not support modern outfits or cards.

## Character Example

```js
{
  id: "MAW-NAM-001", name: "Zahara",
  nation: "Mawaab", house: "Namar",
  ability: "Silver Tongue",
  skin: 3, hair: 0, face: 0, eyes: 0,
  outfit: 0, jewelry: 1, expression: 0, hairColor: 0,
  seed: 48129
}
```

Modern records add `setting: "modern"`, `city`, `team`, and `profession`. Both settings support `archetype`, `body`, `beard`, and `lighting`.

Exported records include the renderer version. Reproduction requires the same record and matching renderer; a seed alone is not a complete character identity. Browser font rasterization can differ, so this is not a guarantee of byte-identical PNGs across machines.

## Extend

Start by adding records to a preset collection. For another visual setting, add a palette, outfit/environment drawing branch, and appropriate editable fields. Reuse the core portrait functions. Do not overwrite an existing character's ID when changing their appearance.

To refine the artwork, edit the relevant drawing function and compare the existing cast. Version renderer changes that intentionally alter saved characters' appearance.

## Checks

```sh
node test.mjs
```

Checks syntax, deterministic collection generation, JSON record round trips, drawing commands, appearance variations, and export dimensions. These are not visual/browser tests.

## Limits And Release Status

## KOSMOS Studio Moments

Open **View Moment** in the gallery toolbar. Each of the five collections has a scene using the same player. Replay restarts dialogue; questions interrupt it with a response. Escape or Close returns to the gallery. Reduced-motion mode keeps the illustrations still.

Export scene JSON, edit it, then import it to create another Moment without editing the renderer. Scene version 1 accepts `title`, `backgroundId`, one or two `characterIds`, ordered `dialogue` entries (`speaker`, `text`), and up to three `interactions` (`label`, `speaker`, `response`). Speaker indices begin at zero. Use character IDs from exported character JSON. Backgrounds reference an existing character's procedural environment.

Scene JSON references characters; it does not embed their appearance. Keep the corresponding character JSON and matching HTML renderer with the scene. Import customized characters before importing their scene. Scenes and edits are held in memory until exported; there is no publishing or persistence service. This is a working-name prototype, not a full editor.

## Episodes And Voice Studio v1

### Guided Voice Production

Episodes open in Voice Studio; **Watch episode** switches to the player. The scrollable dialogue timeline shows script previews, source, decoded duration, and usable saved-line counts. **Record All Missing Lines** visits lines lacking matching, playable saved audio. Saving advances to the next outstanding line, including skipped lines. Microphone access is still requested only when Record is pressed.

The booth presents source alternatives and state-appropriate controls: Record, Stop, Preview, then Save & next or Retake. A new take stays separate from the saved clip until replacement is confirmed. Shot framing and fallback duration are under Shot settings. Browser TTS is only a preview and never counts as saved audio. Changed scripts, missing blobs, and decoding failures remain outstanding rather than being presented as ready.

Character voice profiles are keyed by character ID and stored in the same IndexedDB database through a non-destructive version-2 upgrade. They hold performer, accent, delivery, optional reference audio, and browser voice preference. Profiles and reference audio travel with episode exports; earlier packages without profiles/source metadata still import. Reference recordings guide performances, not voice conversion or cloning. Importing over a locally saved episode requires confirmation.

Storage and source/status logic have automated mocked tests, including six sequential saved lines and profile persistence. Live recording, permission dialogs, actual codecs, export/import on a device, and mobile visual layout require manual testing.

Open **Episodes**. The Last Ember uses Isaac Reed and Wei as an apprentice in two connected scenes: 38 seconds of silent playback, plus the decision pause. The Station Window demonstrates the same player in a different setting. Choose a shot to edit its dialogue, speaker, camera framing, or silent duration. Wide, medium, close-up, and gentle pan/zoom preserve character proportions. Existing Moments and card exports remain separate.

For each line, press Record, grant browser microphone permission, then Stop recording. Preview or Retake, then Save & next. Upload audio follows the same preview/save workflow. Clips are limited to 60 seconds and 25 MB. Only saved takes are included in export. Browser voice preview uses the device's speech synthesis, is not saved, and is not exported. There are no bundled recordings; the demonstration initially plays with captions and silent talking animation.

Unsaved takes are now recovered locally by episode and dialogue ID using separate draft keys in the existing audio store. Reopening a line restores its take without replacing saved audio. Recording chunks are checkpointed as the browser provides them; refresh warns while recording or draft writes are pending. Stop recording and let local writes finish before refreshing. Browser termination, unavailable storage, or a codec that withholds chunks until Stop can still prevent recovery of the last unfinished segment. Drafts are not included in cloud snapshots or portable exports: use Save & next first. Retaking or replacing an unsaved take requires confirmation. Mobile playback keeps captions visible.

Saved recordings and edits live in IndexedDB on the current browser origin. Clearing browser data removes them; another browser or URL does not share them. Export the portable `.kosmos.json` package to preserve the episode, character snapshots, hierarchy metadata, and base64 audio assets. Import validates the content and audio before an atomic storage transaction. Total audio is limited to 75 MB. Matching renderer versions are required for identical artwork. The single HTML download still includes the full engine and Episode view.

Each show contains seasons, episodes, scenes, and shots. Shots carry stable dialogue IDs, background and character references, props, camera framing, cues, duration, and optional decisions. Audio is keyed by episode and dialogue ID with matching text/speaker metadata. Edited dialogue or a different branch response will not play an outdated recording; those lines use captioned silent animation until recorded to match. Audio-driven shot timing follows the saved clip plus a short reaction beat. Only the forge prop is supported in v1; this is not a timeline or scene editor.

Microphone capture requires browser support and permission, normally on HTTPS or localhost. If unavailable on a local file, use upload or serve the same HTML from localhost. Audio codec support depends on the browser. No app server, external voice API, account, or remote storage is used. Browser speech synthesis may use the browser/OS voice provider. Sensitive recordings should only be exported or shared with permission.

Automated checks cover episode structure, duplicate IDs, timing, scene JSON round trips, camera drawing commands, unchanged characters, and mocked storage transactions. Actual microphone permissions, audio codecs, device playback, and mobile visual quality still need manual review. No production deployment has been made.

This is a code-drawn 2D illustration prototype, not a 3D renderer or general-purpose game engine. The gallery is limited to 24 cards per collection. Collections and modern people are fictional. No external fonts or artwork are required by the current demo.

Prepared for review, not yet published as an open-source release. A public repository and reuse license still need to be selected before claiming an open-source launch. The demo's HTML source is downloadable; that alone is not an open-source license.
# Website and Cloud Episodes

## Unified Studio

Story, Cast, Scenes, Voices, and Preview are views of the same active episode.
The scene preview stays visible while editing. Preview line and Preview scene
use the existing episode playback and amplitude-driven mouth animation.
View Moment opens an episode scene with the original dialogue and questions.

Cast edits preserve character IDs and update the reusable local library.
Hair, clothing, and accessory thumbnails use the same procedural renderer.
Facial width, jaw width, and eye-spacing controls default to 1, preserving
existing appearances. Scenes and shots can be reordered without changing
their dialogue IDs or moving audio to different lines.

Local save status changes to saved only after the IndexedDB transaction finishes.
Cloud status distinguishes a confirmed snapshot from newer unsaved changes.
Recordings remain keyed by episode ID and dialogue-line ID. Changing a script
or speaker keeps the old recording, but flags it for a retake.

Automated checks use 12 synthetic Wrong Corner audio fixtures to verify storage,
reordering, cast edits, and JSON round trips. They do not validate a user's actual
microphone recordings or substitute for desktop/mobile visual review.

The hosted app lives at `/kosmos/`. Every theme opens its own episode starter
through **Build episode**. Existing local recordings and stable dialogue IDs are
preserved; theme starters use the same renderer, voice studio, and player.

**Save to cloud** creates a private, immutable snapshot of the episode, saved
line audio, and voice profiles. **Cloud saves** restores a snapshot through the
existing validated import workflow. Unsaved takes are not included. Retrying the
same package does not create another snapshot.

Cloud ownership uses the website's anonymous game identity. No account or email
is required. Clearing browser data loses access to that identity's cloud saves.
Export an episode backup before switching browsers or clearing storage. Audio
recorded on the old local file does not automatically move to the website;
export it locally and import it on the hosted app.

Storage is a private Cloudflare R2 bucket, with metadata in existing GAME_DB D1.
No public bucket or public audio URLs. Limits: 8 MB per package (about 5 MB of
audio), 20 snapshots per browser identity, 512 MB total app storage, 60 cloud
requests per identity per hour, 2,000 cloud requests globally per day. Larger
episodes still support local export/import. These caps keep this feature small;
Cloudflare's account-wide free allowances are shared with other workloads and
are not a guarantee against unrelated usage charges.

