aesthetic.computer Public API

Supported public APIs for publishing creative work and reading public Aesthetic Computer data

version 1.1.0 · updated 2026-08-03

MCP Server

Model Context Protocol server for compatible assistants to create, validate, and publish Aesthetic Computer pieces

package: @aesthetic.computer/mcp

install: npx @aesthetic.computer/mcp

view on tangled →

tools

publish_piece — Publish a JavaScript piece to aesthetic.computer

input: {"source":"string","name":"string (optional)"}
output: {"code":"string","url":"string","cached":"boolean"}

publish_kidlisp — Publish KidLisp code to aesthetic.computer

input: {"source":"string"}
output: {"code":"string","url":"string","cached":"boolean"}

publish_clock — Publish a clock melody to aesthetic.computer

input: {"source":"string"}
output: {"code":"string","url":"string","cached":"boolean"}

get_api_info — Fetch the full API documentation

input: {}
output: API documentation object

preview_kidlisp — Validate KidLisp syntax without publishing

input: {"source":"string"}
output: {"valid":"boolean","errors":"string[]","warnings":"string[]","stats":"object"}

resources

aesthetic-computer://piece-template — Starter template for a new aesthetic.computer piece with all lifecycle functions

aesthetic-computer://kidlisp-reference — Quick reference guide for KidLisp syntax and common functions

aesthetic-computer://piece-examples — Examples drawn from popular published pieces

prompts

create-piece — Guided prompt for creating an aesthetic.computer piece

args: name (required), description (required)

create-kidlisp — Guided prompt for creating a KidLisp piece

args: description (required)

configuration examples

Claude Desktop

{
  "mcpServers": {
    "aesthetic-computer": {
      "command": "npx",
      "args": ["-y", "@aesthetic.computer/mcp"],
      "env": {
        "AC_TOKEN": "optional-bearer-token"
      }
    }
  }
}

Claude Code

{
  "mcpServers": {
    "aesthetic-computer": {
      "command": "npx",
      "args": ["-y", "@aesthetic.computer/mcp"]
    }
  }
}

Cursor

{
  "mcpServers": {
    "aesthetic-computer": {
      "command": "npx",
      "args": ["-y", "@aesthetic.computer/mcp"]
    }
  }
}

HTTP endpoints

POST Store KidLisp Code

https://aesthetic.computer/api/store-kidlisp

Publish KidLisp code anonymously and get a short URL for sharing

Authentication: Optional (Bearer token for authenticated users)

Request body · application/json

{
  "source": {
    "type": "string",
    "required": true,
    "description": "KidLisp source code (max 50,000 characters)",
    "example": "(wipe blue)\n(ink red)\n(box 10 10 50 50)"
  }
}

Response

{
  "code": {
    "type": "string",
    "description": "Short code for accessing the piece (e.g. 'abc123')"
  },
  "cached": {
    "type": "boolean",
    "description": "True if code already existed (deduplication)"
  }
}

Publish a KidLisp Piece

Create a simple animated piece with KidLisp

curl -X POST https://aesthetic.computer/api/store-kidlisp \
  -H "Content-Type: application/json" \
  -d '{
    "source": "(wipe blue)\n(ink yellow)\n(circle (/ w 2) (/ h 2) 100)"
  }'
const response = await fetch("https://aesthetic.computer/api/store-kidlisp", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({
    source: "(wipe blue)\n(ink yellow)\n(circle (/ w 2) (/ h 2) 100)"
  })
});

const { code, cached } = await response.json();
console.log(`View at: https://aesthetic.computer/${code}`);
import requests

response = requests.post(
    "https://aesthetic.computer/api/store-kidlisp",
    json={
        "source": "(wipe blue)\n(ink yellow)\n(circle (/ w 2) (/ h 2) 100)"
    }
)

data = response.json()
print(f"View at: https://aesthetic.computer/{data['code']}")

response:

{
  "code": "xyz789",
  "cached": false
}

POST Store Clock Melody

https://aesthetic.computer/api/store-clock

Publish a clock melody string and get a pronounceable short code

Authentication: Optional (Bearer token for authenticated users)

Request body · application/json

{
  "source": {
    "type": "string",
    "required": true,
    "description": "Clock melody string (max 10,000 characters)",
    "example": "c4 d4 e4 f4 g4"
  },
  "melody": {
    "type": "string",
    "required": false,
    "description": "Legacy field name (use 'source' instead)"
  }
}

Response

{
  "code": {
    "type": "string",
    "description": "Pronounceable short code (e.g. 'bako', 'milu')"
  },
  "cached": {
    "type": "boolean",
    "description": "True if melody already existed (deduplication)"
  }
}

Publish a Clock Melody

Store a musical sequence for the clock piece

curl -X POST https://aesthetic.computer/api/store-clock \
  -H "Content-Type: application/json" \
  -d '{
    "source": "c4 e4 g4 c5 g4 e4 c4"
  }'
const response = await fetch("https://aesthetic.computer/api/store-clock", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({
    source: "c4 e4 g4 c5 g4 e4 c4"
  })
});

const { code, cached } = await response.json();
console.log(`Listen at: https://aesthetic.computer/clock~${code}`);
import requests

response = requests.post(
    "https://aesthetic.computer/api/store-clock",
    json={
        "source": "c4 e4 g4 c5 g4 e4 c4"
    }
)

data = response.json()
print(f"Listen at: https://aesthetic.computer/clock~{data['code']}")

response:

{
  "code": "bako",
  "cached": false
}

GET List Chat Messages

https://aesthetic.computer/api/chat-messages

Read recent messages from a chat channel. `system` backs the main `/chat` piece; `clock` backs `laer-klokken` (r8Dio). Results are chronological (oldest → newest) within each page. Paginate further back with `before`.

Authentication: None (public read)

Query parameters

instance · string
Chat channel to read. `all` spans both channels when `search` or `q` is present. Default: system. Values: system, clock, all.
limit · number
How many messages to return. Values over 100 return HTTP 400. Default: 50.
before · string
ISO-8601 timestamp. Returns messages strictly older than this — pass the `nextBefore` from the previous response to page back.
search · string
Case-insensitive substring search over message text. Search results are newest first.
q · string
Alias for `search`.
from · string
Only return messages sent by this `@handle`.

Response

{
  "instance": {
    "type": "string",
    "description": "Echoes the queried channel."
  },
  "search": {
    "type": "string",
    "description": "Echoes the search term when supplied; omitted otherwise."
  },
  "count": {
    "type": "number",
    "description": "Number of messages in this page."
  },
  "messages": {
    "type": "array",
    "description": "Chronological (oldest → newest).",
    "items": {
      "id": {
        "type": "string",
        "description": "Mongo ObjectId string."
      },
      "from": {
        "type": "string",
        "description": "`@handle` of the sender, or `anon` if unresolved."
      },
      "text": {
        "type": "string",
        "description": "Message body as posted."
      },
      "when": {
        "type": "string",
        "description": "ISO timestamp."
      },
      "instance": {
        "type": "string",
        "description": "Source channel for this message."
      },
      "hearts": {
        "type": "number",
        "description": "Heart-reaction count from the shared `hearts` collection."
      }
    }
  },
  "nextBefore": {
    "type": "string",
    "description": "ISO timestamp of the oldest message in this page — pass as `before=` to fetch the previous page. `null` when the page is empty."
  }
}

Latest 50 messages from the main chat

Default channel is `system`.

curl "https://aesthetic.computer/api/chat-messages"
const res = await fetch("https://aesthetic.computer/api/chat-messages");
const { messages } = await res.json();
for (const m of messages) console.log(m.when, m.from, m.text);
import requests

data = requests.get("https://aesthetic.computer/api/chat-messages").json()
for m in data["messages"]:
    print(m["when"], m["from"], m["text"])

Latest chat from one fighter

curl "https://aesthetic.computer/api/chat-messages?from=%40jeffrey&limit=1"
const res = await fetch("https://aesthetic.computer/api/chat-messages?from=%40jeffrey&limit=1");
const { messages } = await res.json();
console.log(messages.at(-1));
import requests

data = requests.get("https://aesthetic.computer/api/chat-messages",
                    params={"from": "@jeffrey", "limit": 1}).json()
print(data["messages"][-1] if data["messages"] else None)

Latest 100 from the laer-klokken (clock) channel

curl "https://aesthetic.computer/api/chat-messages?instance=clock&limit=100"
const res = await fetch(
  "https://aesthetic.computer/api/chat-messages?instance=clock&limit=100"
);
const { messages, nextBefore } = await res.json();
console.log(messages.length, "messages; older page cursor:", nextBefore);
import requests

data = requests.get(
    "https://aesthetic.computer/api/chat-messages",
    params={"instance": "clock", "limit": 100},
).json()
print(len(data["messages"]), "messages; older page cursor:", data["nextBefore"])

Paginate back through older messages

Use `nextBefore` from each response to walk back in time.

curl "https://aesthetic.computer/api/chat-messages?instance=clock&limit=100&before=2026-04-20T00%3A00%3A00Z"
async function* allClockMessages() {
  let before;
  while (true) {
    const url = new URL("https://aesthetic.computer/api/chat-messages");
    url.searchParams.set("instance", "clock");
    url.searchParams.set("limit", "100");
    if (before) url.searchParams.set("before", before);
    const res = await fetch(url);
    const page = await res.json();
    if (page.count === 0) break;
    yield page.messages;
    before = page.nextBefore;
  }
}
import requests

def all_clock_messages():
    before = None
    while True:
        params = {"instance": "clock", "limit": 100}
        if before:
            params["before"] = before
        page = requests.get("https://aesthetic.computer/api/chat-messages",
                            params=params).json()
        if page["count"] == 0:
            break
        yield page["messages"]
        before = page["nextBefore"]

Search both public chat channels

curl "https://aesthetic.computer/api/chat-messages?instance=all&search=music&limit=25"
const url = new URL("https://aesthetic.computer/api/chat-messages");
url.search = new URLSearchParams({ instance: "all", search: "music", limit: "25" });
const { messages } = await fetch(url).then((res) => res.json());
console.log(messages);
import requests

data = requests.get(
    "https://aesthetic.computer/api/chat-messages",
    params={"instance": "all", "search": "music", "limit": 25},
).json()
print(data["messages"])

Notes

POST Store JavaScript Piece

https://aesthetic.computer/api/store-piece

Publish a JavaScript piece (.mjs) anonymously by providing source code as a string. No S3 credentials needed - the server handles storage automatically.

Authentication: Optional (Bearer token for authenticated users)

Request body · application/json

{
  "source": {
    "type": "string",
    "required": true,
    "description": "JavaScript piece source code (max 100,000 characters). Must contain a supported lifecycle export or a default export.",
    "example": "export function boot($) { $.wipe('blue'); }\nexport function paint($) { $.ink('red'); $.box(10, 10, 50, 50); }"
  },
  "name": {
    "type": "string",
    "required": false,
    "description": "Optional name for the piece (used for code generation)"
  }
}

Response

{
  "code": {
    "type": "string",
    "description": "Short code for accessing the piece (e.g. 'drift', 'wave')"
  },
  "cached": {
    "type": "boolean",
    "description": "True if code already existed (deduplication)"
  },
  "url": {
    "type": "string",
    "description": "Full URL to view the piece"
  }
}

Publish a Simple Piece

Create a piece with basic drawing

curl -X POST https://aesthetic.computer/api/store-piece \
  -H "Content-Type: application/json" \
  -d '{
    "source": "export function boot($) {\n  $.wipe("blue");\n}\n\nexport function paint($) {\n  $.ink("red");\n  $.box(10, 10, 50, 50);\n}",
    "name": "red-box"
  }'
const source = `export function boot($) {
  $.wipe("blue");
}

export function paint($) {
  $.ink("red");
  $.box(10, 10, 50, 50);
}`;

const response = await fetch("https://aesthetic.computer/api/store-piece", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({
    source,
    name: "red-box"
  })
});

const { code, url, cached } = await response.json();
console.log(`View at: ${url}`);
import requests

source = """export function boot($) {
  $.wipe("blue");
}

export function paint($) {
  $.ink("red");
  $.box(10, 10, 50, 50);
}"""

response = requests.post(
    "https://aesthetic.computer/api/store-piece",
    json={
        "source": source,
        "name": "red-box"
    }
)

data = response.json()
print(f"View at: {data['url']}")

response:

{
  "code": "red-box",
  "cached": false,
  "url": "https://aesthetic.computer/red-box"
}

Publish Interactive Piece

Create a piece with user interaction

curl -X POST https://aesthetic.computer/api/store-piece \
  -H "Content-Type: application/json" \
  -d '{
    "source": "let x = 0;\n\nexport function boot($) {\n  x = $.screen.width / 2;\n}\n\nexport function paint($) {\n  $.wipe("black");\n  $.ink("yellow");\n  $.circle(x, $.screen.height / 2, 20);\n}\n\nexport function act($) {\n  if ($.event.is("touch")) x = $.event.x;\n}"
  }'
const source = `let x = 0;

export function boot($) {
  x = $.screen.width / 2;
}

export function paint($) {
  $.wipe("black");
  $.ink("yellow");
  $.circle(x, $.screen.height / 2, 20);
}

export function act($) {
  if ($.event.is("touch")) x = $.event.x;
}`;

const response = await fetch("https://aesthetic.computer/api/store-piece", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({ source })
});

const { code, url } = await response.json();
console.log(`View at: ${url}`);
import requests

source = """let x = 0;

export function boot($) {
  x = $.screen.width / 2;
}

export function paint($) {
  $.wipe("black");
  $.ink("yellow");
  $.circle(x, $.screen.height / 2, 20);
}

export function act($) {
  if ($.event.is("touch")) x = $.event.x;
}"""

response = requests.post(
    "https://aesthetic.computer/api/store-piece",
    json={"source": source}
)

data = response.json()
print(f"View at: {data['url']}")

response:

{
  "code": "touch",
  "cached": false,
  "url": "https://aesthetic.computer/touch"
}

POST Track Media (Publish Artwork)

https://aesthetic.computer/api/track-media

Register an uploaded painting, piece, or tape and receive a short code. The file must already exist in Aesthetic Computer storage.

Authentication: Optional (Bearer token for authenticated users)

Request body · application/json

{
  "slug": {
    "type": "string",
    "required": true,
    "description": "S3/storage path where the file was uploaded"
  },
  "ext": {
    "type": "string",
    "required": true,
    "enum": [
      "png",
      "mjs",
      "lisp",
      "lua",
      "zip",
      "mp4"
    ],
    "description": "Media type: PNG painting; MJS, Lisp, or Lua piece; ZIP web tape; or finished MP4 native tape"
  },
  "metadata": {
    "type": "object",
    "required": false,
    "description": "Optional metadata (for tapes: totalDuration in seconds, max 30s)",
    "properties": {
      "totalDuration": {
        "type": "number",
        "description": "Duration in seconds (tapes only, max 30)"
      }
    }
  }
}

Response

{
  "code": {
    "type": "string",
    "description": "Short code for accessing the media"
  }
}

Publish a JavaScript Piece

After uploading .mjs file to S3, register it in the database

curl -X POST https://aesthetic.computer/api/track-media \
  -H "Content-Type: application/json" \
  -d '{
    "slug": "2026/02/12/my-piece.mjs",
    "ext": "mjs"
  }'
// Step 1: Upload your .mjs file to S3 (requires credentials)
// Step 2: Register the uploaded file
const response = await fetch("https://aesthetic.computer/api/track-media", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({
    slug: "2026/02/12/my-piece.mjs",
    ext: "mjs"
  })
});

const { code } = await response.json();
console.log(`View at: https://aesthetic.computer/${code}`);
import requests

# After uploading your .mjs file to S3
response = requests.post(
    "https://aesthetic.computer/api/track-media",
    json={
        "slug": "2026/02/12/my-piece.mjs",
        "ext": "mjs"
    }
)

data = response.json()
print(f"View at: https://aesthetic.computer/{data['code']}")

response:

{
  "code": "abc456"
}

Publish a Painting (PNG)

Register a painting image after uploading to S3

curl -X POST https://aesthetic.computer/api/track-media \
  -H "Content-Type: application/json" \
  -d '{
    "slug": "2026/02/12/my-painting.png",
    "ext": "png"
  }'
const response = await fetch("https://aesthetic.computer/api/track-media", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({
    slug: "2026/02/12/my-painting.png",
    ext: "png"
  })
});

const { code } = await response.json();
console.log(`View at: https://aesthetic.computer/${code}`);
import requests

response = requests.post(
    "https://aesthetic.computer/api/track-media",
    json={
        "slug": "2026/02/12/my-painting.png",
        "ext": "png"
    }
)

data = response.json()
print(f"View at: https://aesthetic.computer/{data['code']}")

response:

{
  "code": "def789"
}

Publish a Recording Tape (ZIP)

Register a recording after uploading ZIP to S3

curl -X POST https://aesthetic.computer/api/track-media \
  -H "Content-Type: application/json" \
  -d '{
    "slug": "2026/02/12/my-recording.zip",
    "ext": "zip",
    "metadata": {
      "totalDuration": 15.5
    }
  }'
const response = await fetch("https://aesthetic.computer/api/track-media", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({
    slug: "2026/02/12/my-recording.zip",
    ext: "zip",
    metadata: {
      totalDuration: 15.5  // seconds (max 30)
    }
  })
});

const { code } = await response.json();
console.log(`Watch at: https://aesthetic.computer/${code}`);
import requests

response = requests.post(
    "https://aesthetic.computer/api/track-media",
    json={
        "slug": "2026/02/12/my-recording.zip",
        "ext": "zip",
        "metadata": {
            "totalDuration": 15.5  # seconds (max 30)
        }
    }
)

data = response.json()
print(f"Watch at: https://aesthetic.computer/{data['code']}")

response:

{
  "code": "ghi012"
}

Limits and behavior