Skip to main content

Command Palette

Search for a command to run...

Digital Being - Type 2 - Harness UI Prototype - Phase 1

Published
6 min read
Digital Being - Type 2 - Harness UI Prototype - Phase 1

Yesterday, we created a mockup of Harness UI.

1. Purpose

The Harness UI is a simulation and inspection interface for a single Type 2 Digital Being inside a persistent 2D pixel world. It should let us (1) observe the being in real time, (2) inspect internal state and body variables, (3) communicate with the being, (4) inject actions or test scenarios, (5) debug decision-making and body-state transitions, and (6) present the system clearly to possible audience without overwhelming them. The UI is both a debug harness and a presentation surface.

2. World Model

World is 1024x512 pixels. The world is rendered in pixel-art style. The world should preserve crisp pixel edges with no smoothing. Camera behavior should initially be fixed and stable.

The world contains at least these layers:

  • Foreground

    • Visual: RGB (Color), A (Occupancy)

    • Cell Type: None (0), Soil (1), Water (2)

  • Background

    • Visual: RGB (Color), A (Occupancy)

    • Cell Type: None (0), TreeBranch (128), TreeRoot (129), LeafPoint (130), Leaf (131).

  • Far

    • Visual: RGB (Color)

These layers should be available as png files that can be edited by likes of Adobe Photoshop.

3. Entity Model

The first version supports one active Type 2 Digital Being in the scene. The being should be rendered using premade sprite-based animation. Depending on state and elapsed state, it will show different animation sequences.

3.1 Animation Sequences

  • Idle (Standing/Sitting)

  • Moving (Walking/Running)

  • Eating

  • Dead

3.2 Special Effects

  • Hungry

3.3 Physiology

Entity has modules--heart, brain, lung, stomach, muscle.

3.3.1 Lung Module

Lung Module simulates "breath" tick. Every breath, it will push the oxygen level up to, say, 1.0.

{
  oxygen: 0.5,  // Takes from air.
  glucose: 0.5, // Takes from blood flow (heart)
  tickRate: 0.23,   // 12-16 breaths per minute
}

3.3.2 Stomach Module

Stomach Module simulates "digestion" tick. When user eats food, it creates stream of food. On each tick, food moves along the stomach track, then get digested and get turned into glucose. When this conversion happens, oxygen is used. Excess food at the end of track disappears next tick.

{
  oxygen: 0.5,  // Takes from blood flow (heart).
  glucose: 0.5, // Replenished by consuming food.
  track: [0, 0, 0, 2.2, 2.2, …, 0],  // Stomach content.
  tickRate: 0.05,   // 3 waves per minute
}

3.3.3 Heart Module

Heart Module's tick is called a "beat". When this beat happens, it moves oxygen from lung to itself at a rate. It also moves glucose from stomach to itself.

{
  oxygen: 0.5,  // Takes from lung module.
  glucose: 0.5, // Takes from stomach module.
  tickRate: 1.2,    // 60-100 beats per minute.
}

Since modules have different tick rates. Let's have the "beat" tick handle pushing of the oxygen/glucose to various organs.

3.3.4 Brain Module

Brain is viewed as a cost center. On each tick (let's call it "current" tick), it will consume oxygen and glucose. We also add a penalty for LLM inferencing. For each token usage, we will increase the consumption rate ~5-10% of stable rate.

{
  oxygen: 0.5,  // Takes from blood flow (heart).
  glucose: 0.5, // Takes from blood flow (heart).
  tickRate: 1.0,    // 1 per second.
}

3.3.5 Muscle Module

{
  oxygen: 0.5,  // Takes from blood flow (heart).
  glucose: 0.5, // Takes from blood flow (heart).
  tickRate: 1.0,    // 1 per second.
}

Similarly to brain module, muscle is viewed as a cost center. It drains oxygen and glucose at constant rate. Depending on state, it will draw more.

3.3.6 Global Tick (i.e. Heart Beat)

In practice, it is easier to have a single simulation tick that updates all organs at once. Thus, there is only one global heartbeatRate. If it increases, the whole internal simulation speeds up.

heartbeatRate is modulated by the being's current physiological demand and arousal state. It responds to (1) muscle activity, (2) brain load, (3) oxygen deficit, and (4) glucose deficit.

3.3.7 activity Variable and consumptionRate

For each organ, it would be convenient to add "activity" which reflects amount activity happening in that organ. Then, we can use this variable to determine the oxygen/glucose consumption rate. For each organ, we will also need to hard code base oxygen and glucose consumption rates.

{ 
  …,
  activity: 0.25,   // 0.25 resting state, 1.0 would be max.
  consumes: {
    oxygen: 0.2,
    glucose: 0.1,
  }
}

3.3.8 Pseudo Code


interface Organ {
  oxygen: number;   // 0.0 - 1.0
  glucose: number;  // 0.0 - 1.0
  activity: number; // 0.0 - 1.0
}
interface Brain extends Organ {}
interface Heart extends Organ {}
interface Lung extends Organ {}
interface Stomach extends Organ {
  track: number[]; // 0.0 - 1.0 (length: 8)
}
interface Muscle extends Organ {}
interface Body {
  brain: Brain;
  heart: Heart;
  lung: Lung;
  stomach: Stomach;
  muscle: Muscle;
  beatRate: number;
}

/** Create Body */
function makeBody() {
  const body:Body = {
    brain: { oxygen: 0.5, glucose: 0.5, activity: 0.25 },
    heart: { oxygen: 0.5, glucose: 0.5, activity: 0.25 },
    lung: { oxygen: 0.5, glucose: 0.5, activity: 0.25 },
    stomach: { 
      oxygen: 0.5, 
      glucose: 0.5, 
      activity: 0.25, 
      track: new Array(8).fill(0),
    },
    muscle: { oxygen: 0.5, glucose: 0.5, activity: 0.25 },
    beatRate: 1.0,
  };
  return body;
}

const constants = {
   baselines: {
     oxygen: 0.5,
     glucose: 0.5,
     beatRate: 1.0
   }
};

const clp = (v, min=0.0, max=1.0) => Math.min(Math.max(v, min), max);

/** Simulate */
function bodyTick(body: Body) {
  const { brain, heart, lung, stomach, muscle } = body;
  const organs = [brain, heart, lung, stomach, muscle];
  const { baselines: B } = constants;
  const N = organs.length;

  // Terminal state
  if (isTerminal(body)) {
    body.beatRate = 0.0;
    return;
  }

  // Compute resource consumption based on 
  // activity and preset consumption rates. 
  consumptionTick(body);

  // Stomach tick (add glucose and move food along)
  stomachTick(body);

  // Lung tick (add oxygen)
  lungTick(body);

  // Heart tick (push oxygen and glucose)
  heartTick(body); 

  // Compute deficit 
  const deficit = organs.reduce((acc, o) => ({
    oxygen: clp(acc.oxygen + clp(B.oxygen - o.oxygen) / N),
    glucose: clp(acc.glucose + clp(B.glucose - o.glucose) / N),
  }), { oxygen: 0.0, glucose: 0.0 })

  // Compute new heart rate
  const heartBeatRateTarget = B.beatRate + 
    deficit.oxygen * 0.1 + 
    deficit.glucose * 0.1 +
    muscle.activity * 0.2 +
    brain.activity * 0.1;
  const newHeartRate = ...
}

Let's close it at that and move on to rest of the sections.

3.4 Behavior / Control System

In Phase 1, we won't implement control system. Instead, we will add "feed" button that will feed 1.0 to the organism. This would allow use to see:

  • Heart rate modulation.

  • Digestive system working.

  • Respiratory system working.

  • Resource decay.

  • Terminal state.


That's the plan. Let me get to implementation.

-- Sprited Dev 🐛