The whole contract, in one place

Hatch a character.
Keep your page yours.

Peekling is a dependency-free browser runtime for data-only animated characters. Start in two lines, then opt into exactly as much control as your product needs.

00 · THE LANGUAGE

A character type built for gentle company.

In this documentation, a Peekling is a small, data-only animated character designed to accompany a person while leaving them in control. Peek is the first Peekling. peek is the quiet curiosity that gives the world its name.

peek
the idea and action
Peek
the first character
Peekling
the character type
Peeklings
the collective world
Small presence. Genuine personality. Visitor control.

This vocabulary informs runtime choices such as pointer-transparent rendering, reduced motion, optional reactions, and telemetry-free operation. Read the full philosophy →

01 · CHOOSE A GUIDE

Go straight to the work in front of you.

The overview explains how Peekling fits together. The guides below turn that contract into practical steps for each kind of work.

Artists
Prepare original art, animation states, and a valid Pack.
Creators
Design an interaction in the playground and hand off its Plan.
Developers
Install the runtime, connect application events, and ship it safely.
Companies
Evaluate fit, ownership, rollout controls, and current limits.
Consumers
Understand what a Peekling observes and how to let it rest.
Researchers
Build a reproducible study around an inspectable runtime.
Specifications
Find the versioned contracts and sources of truth.
Contributors
Choose the right repository and prepare a focused change.

Enterprise, Desktop, and Browser Extension pages explain the work in progress, the intended product boundaries, and where contributors can help.

02 · QUICK START

Two lines, one companion.

Pin an exact runtime version in production. Official characters such as Peek resolve through the versioned registry and fetch only one appropriate atlas.

<script defer src="https://cdn.jsdelivr.net/npm/@peekling/runtime@0.1.0/dist/peekling.min.js"></script>
<peekling-character character="peek"></peekling-character>
Runtime 0.1.0 is live on npm, jsDelivr, and UNPKG. This website installs the exact npm release and serves those same browser bytes from its own origin for local development.

03 · ARCHITECTURE MAP

One narrow path from input to pixels.

Browser-specific modules collect input, load files, and draw. Pure core modules validate data, evaluate behavior, arbitrate competing requests, and resolve states. That boundary keeps packs inert and gives future hosts a stable core while preserving the browser API.

Host pagePointer, touch, scroll, host API
Input collectorPassive normalized events
WorldImmutable snapshot
Plan rulesTyped conditions and effects
Channel compositionState, motion, and surfaces
ResolverCapability and safe fallback
Atlas rendererClosed, pointer-free host

How the files connect

character.json
Identity, description, states, capabilities, timings, density variants, and hashes.
atlas-1x/2x/4x.png
The same densely packed frame order at increasing source density. Only one is fetched.
LICENSE
The artwork’s rights statement. Separate from the Apache-2.0 runtime license.
NOTICE
Public attribution and legal notices shipped with the character package.
hatch()Alias or manifest URL
ManifestParse and validate
DensityChoose smallest adequate variant
AtlasMIME, bytes, geometry, hash
InstanceReady, emit, override, destroy

04 · CONFIGURATION

Simple first. Precise when needed.

const friend = Peekling.hatch({
  format: 1,
  character: "peek",
  position: "bottom-right",
  plan: {
    baseline: {
      channels: ["state"],
      state: { state: "idle" }
    },
    rules: [
      {
        id: "show-success",
        when: { source: "application", event: "app.success" },
        effect: {
          channels: ["state"],
          state: { state: "success" },
          until: { type: "duration", ms: 650 }
        }
      },
      {
        id: "follow-pointer",
        when: { source: "browser", event: "pointer.move" },
        effect: {
          channels: ["motion", "state"],
          motion: { type: "follow-pointer", speed: 160, arrivalRadius: 24 },
          state: { capability: "locomotion" }
        }
      }
    ]
  },
  maxDensity: 4,
  onDiagnostic(message) { console.warn(message); }
});
plan is the only behavior model. The baseline always owns a valid State. Rules respond to typed browser or application Events while the character Pack remains declarative data.
character / packUrl / pack
Select one registered character, explicit manifest URL, or inline validated pack. Explicit sources remain self-hostable.
plan
One immutable baseline plus optional declaration-ordered rules. Effects may own state, motion, or named content surfaces.
scale
Logical render multiplier from 1 through 4. Display size changes while source-art quality stays tied to atlas density.
position
Initial center position. Use bottom-right (the default), bottom-left, center, or finite { x, y } CSS-pixel coordinates. Coordinates clamp safely inside the viewport.
density / maxDensity
Optional 1×, 2×, or 4× controls for testing or download policy. Automatic selection is recommended.
styles
An explicit runtime stylesheet URL and optional integrity value for bundlers, strict CSP, or another allowed origin.
content / bindings
Safe built-in text and links, plus trusted property-only mounts owned by the host application.
diagnostics / onDiagnostic
Structured diagnostic policy or a compact message callback. Pack and configuration failures still reject readiness.

05 · MOUSE AND TOUCH

Different controls. One safe intent stream.

Passive Pointer Events feed one bounded input collector. Mouse, touch, and pen update the same typed world state. Touch and pen pursuit clears on release or cancellation. Hosts keep ownership of product-specific gestures and report later results through emit().

MousePointer movement and click
Touch and penContact, release, and cancellation
Typed browser eventBounded immutable input
Plan evaluationPlan-controlled animation work
const friend = Peekling.hatch({
  character: "peek",
  plan: {
    baseline: { channels: ["state"], state: { state: "idle" } },
    rules: [{
      id: "pointer-click",
      when: { source: "browser", event: "pointer.click" },
      effect: {
        channels: ["state"],
        state: { state: "click" },
        until: { type: "duration", ms: 650 }
      }
    }]
  }
});
Native pan, zoom, selection, context menus, focus, and event propagation keep their browser behavior. v0.1 observes browser-generated events. Product-specific double-tap, long-press, and haptic interactions belong to the host UI, where capability checks can select the appropriate response.

06 · INSTANCE API

Your lifecycle stays explicit.

await friend.ready;
friend.emit("app.success", { documentId: "guide" });
const preview = friend.override({
  effect: { channels: ["state"], state: { state: "move:E" } },
  until: { type: "duration", ms: 1200 }
});
await preview.finished;
friend.destroy();
console.log(await friend.finished);

Every hatch call creates an independent instance. ready settles after validation and the first atlas load. emit() admits a bounded application event. override() temporarily owns declared channels, then releases them back to the unchanged Plan. pause() and resume() compose with visibility suspension. destroy() is idempotent and removes the host, listeners, animation work, loads, and object URLs.

07 · CHARACTER PACKS

Art and capabilities arrive as data.

A native Pack is declarative character data with a small, inspectable shape:

character.json
atlas-1x.png
atlas-2x.png   # optional
atlas-4x.png   # optional
LICENSE
NOTICE

Atlases have 16 columns and fixed square cells. The manifest maps state names to explicit packed cell IDs, timing, eight directional locomotion capabilities and optional bounded movement choreography. Authoring rows stay in the art workflow, and Plan Rules define runtime behavior.

08 · STATES AND MOVEMENT

The artist chooses how movement feels.

A state holds 1–64 frames and uses either uniform FPS or one bounded duration per frame. It may loop or hold its final frame. Locomotion maps all eight directions to arbitrary names, hop, roll, glide, drive, or anything else. The engine follows the movement language declared by the Pack.

Optional motion keyframes describe normalized phase, forward advance, and bounded lift as validated data. A missing requested State resolves predictably through an allowed mirror, semantic alias, idle, then the first valid State.

09 · SHARPNESS AND DENSITY

One logical size. The right source pixels.

Peekling calculates required density from device pixel ratio × render scale, downloads the smallest adequate 1×/2×/4× atlas, and preserves position, state, frame, and timing if a sharper atlas is needed after a zoom or display change. Save-data mode caps automatic selection at 2×. Only one atlas is fetched at a time.

10 · SECURITY AND PRIVACY

A guest that respects the room.

  • Character Packs are strictly validated data. Runtime code stays inside the reviewed engine.
  • The fixed character host is aria-hidden, unfocusable, and pointer-transparent. Only a disclosed visibility menu accepts input.
  • Observers preserve event propagation and the page's own handlers.
  • Remote MIME, byte size, paths, image geometry, and atlas hashes are checked.
  • The runtime is telemetry-free and works independently of an account, cookie, or Peekling backend.
  • Reduced motion renders a static tableau. Idle work parks completely.

11 · DISMISSAL

Zero intrusion includes an exit.

Hold the pointer within the bottom-right 64-pixel corner for about one second to reveal Let Peekling rest? Leaving the corner cancels the hold. The gesture works independently of the character. Its invisible zone stays pointer-transparent, leaving the host page fully interactive.

Visitors can hide every Peekling on this site for 10 minutes, 1 hour, until tomorrow, until the browsing session ends, or forever. The preference stays in local or session storage on the current site. It remains independent of accounts, cookies, network requests, and telemetry. Keyboard users can reach the same menu through its focus-revealed corner control.

Peekling.visibility.hide("session");
Peekling.visibility.show();
Peekling.visibility.isHidden();
Want your companion back? This clears the local rest preference for this site.

12 · HOSTING

npm stores it. jsDelivr delivers it.

Official runtime and character files originate in exact-version public npm packages. Browsers can request cached files from jsDelivr's CDN edge. Under current public OSS terms, this path carries zero direct usage-metered hosting cost for Peekling or site owners. Exact-version self-hosting gives teams control if provider availability or pricing changes.

Community characters may live in independent npm packages or at an immutable HTTPS URL. Pass their manifest with packUrl. CORS and the host page’s CSP still apply.

13 · COMPATIBILITY

Browser-native by design.

Peekling uses standard browser APIs and works regardless of whether a website’s server uses Node, Rust, PHP, Python, Ruby, Java, or Go. Framework wrappers are optional lifecycle conveniences. The separate, lazy Codex Pet v2 adapter can validate and normalize supported pet.json and spritesheet.webp bundles. The adapter loads only when a host requests that format.

Try the current release

Give your interface a pulse.

Copy the two-line install ↓

14 · BUILD WITH US

Using Peekling? We would love to see it.

Peekling grows in public on GitHub. Pick the action that fits the time you have and leave a useful signal for the next builder.

Using Peekling?
Open an issue with a link, screenshot, or short description of the experience you made.
Want to support it?
Star the repository. It gives new visitors a quick, public signal that people care about the work.
Ready to contribute?
Choose one focused improvement to the engine, docs, or character ecosystem. Merged work remains visible in the repository history.

Open source and community

Meet the project on GitHub.

Visit Peekling on GitHub →