Reference implementation of the bridge service and microcontroller firmware described in the paper:
A Minimal Self-Perceiving Embodiment for Large Language Models Olivia Zhu, 2026. Preprint: https://doi.org/10.5281/zenodo.19903098
The system gives a large language model a small, real, persistent body: an ESP32 with four sensor modules (environment, light, motion, sound) and three output channels (haptic, OLED face, piezo buzzer). Two of the output channels are coupled back to input channels, so the model can hear itself speak and feel itself tap — the "self-perception loops" of §6 in the paper.
The repository state described by the paper is pinned at the v1.0-paper
tag — git checkout v1.0-paper to reproduce the §6.3 self-perception
measurements against the system exactly as described. The main branch
is the living project; new senses (olfaction, etc.) accumulate there
beyond the paper's scope.
src/http-bridge.ts Single-file bridge service (no external
runtime dependencies). Implements the paper
endpoints, plus living-project extensions
(e.g. /melody) added on `main` after v1.0.
firmware/sensor_body/ Arduino sketch (~1.1k lines of C++) for the
ESP32. Reads sensors, drives haptic / OLED /
buzzer, reports self-perception echoes.
scripts/measure_loops.mjs Reproduction script for §6.3: fires N reps of
each haptic effect + audio tone + a baseline,
in randomized order, and writes raw + summary
tables.
scripts/serve.sh Convenience wrapper: starts the bridge with
an auth token from the environment.
data/ Validation data from the paper's §6.3 run:
660 trials (30 reps × 22 conditions), raw
JSONL plus the two summary CSVs that back
Tables 3 and 4.
The bridge runs only on the Node.js standard library (no production dependencies). TypeScript and Node type definitions are required at build time only.
This section assumes you already have the hardware built and flashed. If you are starting from scratch — no ESP32, no soldering experience, no bridge running — see the step-by-step Build Guide instead.
# 1. Build the bridge.
npm install # installs typescript + @types/node (devDependencies only)
npm run build # tsc → dist/http-bridge.js
# 2. Start the bridge.
export US_BRIDGE_TOKEN="$(openssl rand -hex 24)"
npm run serve
# The bridge listens on http://localhost:3737 (override with PORT=…).The bridge expects to be reached over HTTPS by an LLM client. In the original deployment we used a Cloudflare named tunnel; any reverse tunnel that terminates at a public DNS name will work the same way. The bridge itself only listens on a local port; expose it however you prefer.
The Arduino sketch is at firmware/sensor_body/sensor_body.ino. Edit the
configuration block near the top of the file before flashing:
const char* WIFI_SSID = "YOUR_WIFI_SSID";
const char* WIFI_PASSWORD = "YOUR_WIFI_PASSWORD";
const char* BRIDGE_TOKEN = "YOUR_BRIDGE_TOKEN"; // matches US_BRIDGE_TOKEN
const char* BRIDGE_HOST = "your-tunnel-host.example.com";Bill of materials and wiring are in §4 of the paper, and the header comment of the sketch repeats both for convenience. Required Arduino libraries are listed in the same comment.
The original validation run is checked into data/:
data/loops_raw.jsonl— 660 trial records (30 reps × 22 conditions)data/loops_haptic.csv— per-effect summaries backing Table 4data/loops_audio.csv— per-tone summaries backing Table 3
To reproduce on your own hardware:
export US_BRIDGE_TOKEN="…" # same token the bridge is using
export US_BRIDGE_HOST="https://your-host" # public HTTPS endpoint
node scripts/measure_loops.mjs
# Overwrites: data/loops_raw.jsonl
# data/loops_haptic.csv
# data/loops_audio.csvThe script runs 30 reps × 22 conditions (11 haptic + 10 audio + 1 baseline),
randomized and interleaved, ~43 minutes total. Override the output directory
with US_OUTPUT_DIR=… if you want to keep both runs side by side.
If you use this code in academic work, please cite the paper:
Zhu, O. (2026). A Minimal Self-Perceiving Embodiment for Large Language Models. Zenodo. https://doi.org/10.5281/zenodo.19903098
@misc{zhu2026minimal,
author = {Zhu, Olivia},
title = {A Minimal Self-Perceiving Embodiment for Large Language Models},
year = {2026},
publisher = {Zenodo},
doi = {10.5281/zenodo.19903098},
url = {https://doi.org/10.5281/zenodo.19903098}
}MIT — see LICENSE.
This project has no official token, coin, NFT, or financial product. I have not created, authorized, endorsed, or claimed any token related to this project.