Integration
Tamaclaw
Animated companion display for OpenClaw — a digital pet that speaks, shows emotions, notifications and dashboard charts on a kiosk screen. One npm package: plugin + bridge + display.
Install
npm install
./launch.sh
README
# Tamaclaw 🥚🦞
Animated companion display for [OpenClaw](https://github.com/openclaw) — a
digital pet that lives on a small screen connected to a Mac mini and acts as
the agent's "face." It speaks out loud, shows emotions, pushes notifications,
renders dashboard charts, and displays rich content cards.
One npm package = everything: OpenClaw plugin + HTTP bridge + kiosk display.


[](https://www.npmjs.com/package/tamaclaw)
---
## Table of contents
1. [Quick start (one command)](#quick-start-one-command)
2. [Requirements](#requirements)
3. [Manual installation (step by step)](#manual-installation-step-by-step)
4. [Standalone mode (no OpenClaw)](#standalone-mode-no-openclaw)
5. [Opening the display](#opening-the-display)
6. [Usage examples](#usage-examples)
7. [Pets (skins)](#pets-skins)
8. [REST API reference](#rest-api-reference)
9. [Voice (TTS)](#voice-tts)
10. [Screen wake](#screen-wake)
11. [Native window (no browser)](#native-window-no-browser)
12. [Character behavior](#character-behavior)
13. [Configuration reference](#configuration-reference)
14. [Project structure](#project-structure)
15. [Development](#development)
---
## Quick start (one command)
No need to clone anything. Run this directly from your terminal:
```bash
bash <(curl -fsSL https://raw.githubusercontent.com/gosbx/Tamaclaw/main/launch.sh)
```
Or in Chrome kiosk mode (fullscreen, no browser UI):
```bash
bash <(curl -fsSL https://raw.githubusercontent.com/gosbx/Tamaclaw/main/launch.sh) --kiosk
```
That's it. The script will:
1. Check Node >= 23.6 and `openclaw` CLI
2. Install the `tamaclaw` plugin (if not already installed)
3. Update it (if a newer version exists on npm)
4. Enable the plugin
5. Restart the OpenClaw Gateway
6. Wait for the bridge to come up on port 4321
7. Open the display in your browser
8. Return control to your terminal
If OpenClaw is not installed, it switches to standalone mode automatically.
**From a local clone** (same thing, but offline):
```bash
git clone https://github.com/gosbx/Tamaclaw.git
cd Tamaclaw
npm install
./launch.sh # or: ./launch.sh --kiosk
```
On first launch you pick your pet, and you're done.
---
## Requirements
- **macOS** (TTS and screen wake use macOS APIs)
- **Node.js >= 23.6** (runs TypeScript natively — no build step)
---
## Manual installation (step by step)
If you prefer to do it manually instead of using `./launch.sh`:
### Step 1 — Install the plugin
```bash
openclaw plugins install tamaclaw
```
### Step 2 — Enable it
```bash
openclaw plugins enable tamaclaw
```
### Step 3 — Restart the Gateway
```bash
# however you restart your Gateway — the bridge starts automatically with it
openclaw restart
```
The bridge is now serving the display at `http://localhost:4321`.
### Step 4 — Open the display
On the kiosk Mac (or any browser on the same network):
```bash
open http://localhost:4321
```
On first launch you'll see the **pet picker** — tap a pet to preview it live,
then confirm. Done. The agent now has a face.
### Without npm (private/offline install)
```bash
# from this repo:
npm run pack # → dist/tamaclaw-0.1.3.tgz
openclaw plugins install ./dist/tamaclaw-0.1.3.tgz
openclaw plugins enable tamaclaw
openclaw restart
```
### Local development (symlink)
```bash
openclaw plugins install --link ./packages/tamaclaw
# or: npm run install-plugin (copies to ~/.openclaw/extensions/tamaclaw)
openclaw plugins enable tamaclaw
openclaw restart
```
### Troubleshooting: bridge doesn't start after install/update
The Gateway sometimes doesn't auto-start the bridge after a plugin
install or update. If `http://localhost:4321` doesn't respond, run this
one-liner to start it manually (it stays in the background):
```bash
kill $(lsof -ti :4321) 2>/dev/null; BRIDGE=$(find ~/.openclaw -name "main.js" -path "*/tamaclaw/dist/bridge/*" -type f | head -1) && nohup node "$BRIDGE" > /tmp/tamaclaw-bridge.log 2>&1 & open http://localhost:4321
```
What it does: kills any old bridge on port 4321, finds the installed
bridge binary, starts it in the background (log at `/tmp/tamaclaw-bridge.log`),
and opens the display.
To stop it:
```bash
kill $(lsof -ti :4321) 2>/dev/null
```
---
## Standalone mode (no OpenClaw)
You can run Tamaclaw without OpenClaw — the bridge is a plain HTTP server
that anything with `curl` can drive.
```bash
# Option A: npx (after npm publish)
npx tamaclaw
# Option B: from the repo
git clone https://github.com/gosbx/Tamaclaw.git
cd Tamaclaw
npm install
npm run dev # starts the bridge with --watch on port 4321
```
Open `http://localhost:4321` in a browser and start sending events.
---
## Opening the display
The display is a web page served by the bridge at `http://localhost:4321`.
| Method | Command |
| --- | --- |
| Any browser | `open http://localhost:4321` |
| Chrome kiosk (fullscreen, no UI) | `open -a "Google Chrome" --args --kiosk --app=http://localhost:4321` |
| Native macOS window (no browser needed) | `./scripts/build-shell.sh && dist/Tamaclaw.app/Contents/MacOS/Tamaclaw` |
On first launch, the display shows the **pet picker**. Choose your pet and
you're ready to go. The choice is saved in the browser and persists across
reloads.
---
## Usage examples
All examples use `curl` against the bridge. If you're using OpenClaw, the
agent calls these automatically via its tools (`tamaclaw_say`, etc.).
### Make the pet speak
```bash
curl -X POST localhost:4321/say \
-H 'content-type: application/json' \
-d '{"text": "Hello! The deploy is done.", "mood": "happy"}'
```
The pet animates its mouth (lip-sync), the text appears in a speech bubble,
and the Mac speaks it out loud. Utterances are queued — they never overlap.
### Send a notification toast
```bash
# Info (green, auto-dismisses)
curl -X POST localhost:4321/notify \
-H 'content-type: application/json' \
-d '{"title": "PR approved", "body": "core #142 ready to merge", "level": "info"}'
# Warning (yellow, stays longer)
curl -X POST localhost:4321/notify \
-H 'content-type: application/json' \
-d '{"title": "Disk at 85%", "body": "Data volume growing fast", "level": "warning"}'
# Critical (red, chimes, interrupts current speech)
curl -X POST localhost:4321/notify \
-H 'content-type: application/json' \
-d '{"title": "CI broken on main", "body": "3 tests failing", "level": "critical"}'
```
### Render a dashboard chart
```bash
# Bar chart (simple data: array of {label, value})
curl -X POST localhost:4321/dashboard \
-H 'content-type: application/json' \
-d '{
"widget": "sales_today",
"chart": "bar",
"title": "Today'\''s Sales",
"pin": true,
"data": [
{"label": "09h", "value": 12},
{"label": "11h", "value": 31},
{"label": "13h", "value": 26},
{"label": "15h", "value": 44},
{"label": "17h", "value": 38}
]
}'
# Multi-series line chart
curl -X POST localhost:4321/dashboard \
-H 'content-type: application/json' \
-d '{
"widget": "api_latency",
"chart": "line",
"title": "API Latency (ms)",
"data": {
"labels": ["Mon", "Tue", "Wed", "Thu", "Fri"],
"series": [
{"label": "p50", "values": [120, 115, 130, 110, 105]},
{"label": "p99", "values": [340, 360, 390, 320, 300]}
]
}
}'
# Pie chart
curl -X POST localhost:4321/dashboard \
-H 'content-type: application/json' \
-d '{
"widget": "traffic_sources",
"chart": "pie",
"title": "Traffic Sources",
"data": [
{"label": "Organic", "value": 45},
{"label": "Paid", "value": 30},
{"label": "Referral", "value": 15},
{"label": "Direct", "value": 10}
]
}'
```
Charts with the same `widget` id replace each other (live update). Use
`"pin": true` to keep a chart on screen permanently; unpinned charts expire
after 2 minutes. Multiple charts rotate in a carousel.
### Show a rich content card
The pet steps aside and a card takes center stage — for content the user
should actually read.
```bash
# Email summary
curl -X POST localhost:4321/show \
-H 'content-type: application/json' \
-d '{
"icon": "📧",
"title": "Inbox summary",
"source": "Gmail",
"body": "1. Contract ready to sign\n2. Month-end close pending\n3. RFC awaiting your review",
"say": "You have three important emails",
"ttl": 15000
}'
# Slack message
curl -X POST localhost:4321/show \
-H 'content-type: application/json' \
-d '{
"icon": "💬",
"title": "Message from @alice",
"source": "Slack #engineering",
"body": "Can you review the migration PR before EOD? The staging deploy is blocked on it.",
"say": "Alice is asking you to review a PR"
}'
```
### Show a card with custom HTML
Any HTML renders inside the card (scripts don't execute, max 256KB):
```bash
# Styled comparison table
curl -X POST localhost:4321/show \
-H 'content-type: application/json' \
-d '{
"icon": "📊",
"title": "Q3 vs Q4",
"source": "Finance",
"html": "<table style=\"width:100%;border-collapse:collapse;font-size:1.1em\"><thead><tr style=\"border-bottom:2px solid #555\"><th style=\"text-align:left;padding:8px\">Metric</th><th style=\"text-align:right;padding:8px\">Q3</th><th style=\"text-align:right;padding:8px\">Q4</th></tr></thead><tbody><tr><td style=\"padding:8px\">Revenue</td><td style=\"text-align:right;padding:8px\">$1.2M</td><td style=\"text-align:right;padding:8px;color:#4ade80\">$1.8M</td></tr><tr><td style=\"padding:8px\">Users</td><td style=\"text-align:right;padding:8px\">12,400</td><td style=\"text-align:right;padding:8px;color:#4ade80\">18,700</td></tr><tr><td style=\"padding:8px\">Churn</td><td style=\"text-align:right;padding:8px\">4.2%</td><td style=\"text-align:right;padding:8px;color:#f87171\">5.1%</td></tr></tbody></table>",
"say": "Q4 revenue is up but churn increased",
"ttl":
... (truncated)
integration
Comments
Sign in to leave a comment