pixoo-mcp-server
This server lets you control Divoom Pixoo LED displays over MCP, rendering and pushing pixel-art content to them.
Render and push styled text with themes, gradients, shadows, outlines, auto-fit, effects, and preview images.
Compose layered static or animated scenes from text, icons, shapes, widgets, bitmaps, images, and sprites.
Push any image from a local path or https URL with fit modes and resize kernels.
Set or clear device-native scrolling text overlays that persist across channel switches.
Control device state: brightness, screen on/off, channel, and clock face; also read current status.
Discover Pixoo devices on the local network and verify the configured device IP.
Get design briefs and troubleshooting guidance with live device context and suggested next tools.
Run as an MCP server over STDIO or Streamable HTTP, returning preview images, layout reports, and push acknowledgements.
Allows using Cloudflare R2, KV, or D1 as swappable storage backends for managing server data and display assets.
Enables loading, resizing, and pushing JPEG images directly to Divoom Pixoo LED matrices.
Includes support for OpenTelemetry instrumentation to provide observability and monitoring of server operations.
Provides support for using Supabase as a storage backend provider for server data and configurations.
Supports loading and pushing SVG vector graphics to Divoom Pixoo devices with automatic resizing to the display grid.
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@pixoo-mcp-serverdisplay 'Meeting in Progress' in red scrolling text and set brightness to 50"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
Tools
Seven tools covering the full display pipeline — from quick styled text to full layered scene composition, device control, and initial setup:
Tool | Description |
| Render styled text (theme, gradient, shadow, outline, auto-fit) onto the display and push it. Returns the rendered frame as an image. |
| Compose a full scene: layered elements (text, icons, widgets, shapes, bitmaps, images, sprites) with per-element effects and keyframes, static or animated. Returns the rendered scene as an image. |
| Load an image (absolute local path or https URL), resize it to the LED grid, and push it. Returns the downsampled result as an image. |
| Set or clear a device-native scrolling text overlay. Uses device-rendered fonts; overlays persist across channel switches until cleared. |
| Read or change device state: brightness, screen on/off, channel, or clock face. Call with no params for a status read. |
| Find Pixoo devices on the local network via Divoom's cloud discovery endpoint. Run once during setup to find device IPs. |
| Return craft guidance and live device context for a design topic. Covers legibility rules, palette discipline, layout zones, animation budget, and pre-filled next-tool suggestions. |
pixoo_display_text
The primary tool for text-only display. Covers the 80% case — styled text with quality defaults.
Named scene themes set background gradient and text palette in one parameter (
midnight,ember,claude,ice,neon,forest,mono)Style block: gradient palette ramps (
ember,ice,neon,fire,lavender,claude,mono), drop shadow, 1px outline for legibility, integer scale multiplier for block-letter weightSemantic positioning:
x: "center",y: "bottom"— no manual pixel mathAuto-fit overflow: tries 5×7 → 3×5 → scroll; every fit decision reported in
layout[]Returns the rendered frame as an image content block so you see it immediately
Optional brightness convenience parameter applied before push
pixoo_compose_scene
Full scene composition with the complete element vocabulary.
Layered elements rendered back-to-front:
text,icon,rect,circle,line,progress,sparkline,bitmap,pixels,image,spriteNamed icons from the built-in registry (weather, arrows, status, media) or custom SVG path
Dashboard widgets:
progressbar with gradient fill and optional label;sparklinemini chart (line or bar, auto-scaled)Animation: named effect presets (
float,scroll-left,scroll-right,pulse,blink,twinkle,drift,fade-in,fade-out) or raw keyframe arrays — 1–40 frames, configurable speedPer-element opacity and
visibleflag; images at https URLs fetched server-side to a temp fileReturns a preview image (static: PNG; animated: labeled contact-sheet PNG + GIF saved to disk)
pixoo_push_image
Push any image to the display with control over the downsampling.
Accepts absolute local paths and https URLs
Three fit modes:
contain(letterbox),cover(crop to fill),fill(stretch)Three resize kernels:
nearestfor pixel art,lanczos3for photos,mitchellfor a balanceReturns the exact 64×64 result as an image block — you see what the display received
pixoo_overlay_text
Device-native scrolling text overlay — persists across channel switches.
115 device-rendered font IDs (0–114)
Up to 20 independent overlay slots (IDs 0–19)
Configurable scroll direction, speed, and alignment
Clears with
mode: "clear"— overlays survive channel changes until explicitly removedNot previewable (device-rendered); for styled previewable text use
pixoo_display_text
pixoo_design_brief
The orientation tool. Run before authoring any scene to get grounded in 64px craft constraints.
Six topics:
text,scene,dashboard,animation,pixel-art,troubleshootingReturns legibility floors, palette discipline, layout zones, animation budgets, and common pitfalls
Merges live device state (reachable, channel, brightness, screen) into the response
Pre-filled
nextToolSuggestionswith ready-to-use arguments based on current device state
Related MCP server: mcp-divoom-lan
Resources
Type | Name | Description |
Resource |
| Live snapshot of the connected Pixoo display: reachable, channel, brightness, screen state, and display size |
Resource |
| Theme and palette registry with background gradients, default text palettes, accent colors, and swatch values |
Resource |
| Built-in icon names organized by category (weather, arrows, status, media) |
Resource |
| Long-form 64px craft guide: legibility floors, palette discipline, layout zones, animation budget, and known device behaviors |
All resource data is also reachable via tools. pixoo_design_brief surfaces the design guide content per topic; pixoo_control_device returns live device state equivalent to pixoo://device/status.
Features
Built on @cyanheads/mcp-ts-core:
Declarative tool and resource definitions — single file per primitive, framework handles registration and validation
Unified error handling — handlers throw, framework catches, classifies, and formats
Pluggable auth:
none,jwt,oauthSwappable storage backends:
in-memory,filesystem,Supabase,Cloudflare KV/R2/D1Structured logging with optional OpenTelemetry tracing
STDIO and Streamable HTTP transports, serving MCP protocol revision 2026-07-28 alongside the 2025 revisions
Pixoo-specific:
Requires a Divoom Pixoo LED matrix display on the local network; primary target is the Pixoo-64 (16 and 32 also supported)
All composition happens in an RGBA canvas pipeline on the host (
@cyanheads/pixoo-toolkit) — the device receives final RGB frames, never raw drawing commandsStyled text engine: gradient palette ramps, drop shadows, outlines, integer scale, semantic alignment — no manual pixel math or bitmap letterforms required
Push pacing: device commands serialized with a configurable minimum inter-push interval (default 1000ms) to prevent device freezes
Every
PixooResultchecked —pushed: truemeans the device acknowledged witherror_code: 0, never "I tried"Animation capped at 40 frames (device instability beyond this); contact-sheet PNG preview for animations (GIF inconsistent across MCP clients)
Local transports only —
sharpimage processing doesn't run on Cloudflare Workers
Agent-friendly output:
Preview-as-content: render tools return the upscaled (8×, 512px) output as an image content block — the calling model sees exactly what was drawn, before and after push
Layout transparency: every silent renderer decision (font fallback, truncation, scroll engaged, element clipped) reported in
layout[]so agents can inspect and refineDevice truth:
pushedreflects the device ACK;deviceStatepost-push flags visibility issues (screen off, brightness ≤ 10, wrong channel) as enrichment notices rather than failuresGraceful degradation: render succeeds and returns the preview even when the device is unreachable — the agent keeps its work
Getting started
Requirements: A Divoom Pixoo display (Pixoo-64, Pixoo-32, or Pixoo-16) on the same local network as the server. Run pixoo_discover_devices to find its IP, then set PIXOO_IP in your server configuration.
Add the following to your MCP client configuration file:
{
"mcpServers": {
"pixoo-mcp-server": {
"type": "stdio",
"command": "bunx",
"args": ["@cyanheads/pixoo-mcp-server@latest"],
"env": {
"MCP_TRANSPORT_TYPE": "stdio",
"MCP_LOG_LEVEL": "info",
"PIXOO_IP": "192.168.1.50"
}
}
}
}Or with npx (no Bun required):
{
"mcpServers": {
"pixoo-mcp-server": {
"type": "stdio",
"command": "npx",
"args": ["-y", "@cyanheads/pixoo-mcp-server@latest"],
"env": {
"MCP_TRANSPORT_TYPE": "stdio",
"MCP_LOG_LEVEL": "info",
"PIXOO_IP": "192.168.1.50"
}
}
}
}Or with Docker:
{
"mcpServers": {
"pixoo-mcp-server": {
"type": "stdio",
"command": "docker",
"args": [
"run", "-i", "--rm",
"-e", "MCP_TRANSPORT_TYPE=stdio",
"-e", "PIXOO_IP=192.168.1.50",
"ghcr.io/cyanheads/pixoo-mcp-server:latest"
]
}
}
}For Streamable HTTP, set the transport and start the server:
MCP_TRANSPORT_TYPE=http MCP_HTTP_PORT=3010 PIXOO_IP=192.168.1.50 bun run start:http
# Server listens at http://localhost:3010/mcpPrerequisites
Bun v1.3.2 or higher (or Node.js v24+).
A Divoom Pixoo LED matrix display on the local network (Pixoo-64, Pixoo-32, or Pixoo-16). Discovery tools and pure-render tools (
push: false) work without a configured device.
Installation
Clone the repository:
git clone https://github.com/cyanheads/pixoo-mcp-server.gitNavigate into the directory:
cd pixoo-mcp-serverInstall dependencies:
bun installConfigure environment:
cp .env.example .env
# edit .env and set PIXOO_IPConfiguration
All configuration is validated at startup via Zod schemas in src/config/server-config.ts. Key environment variables:
Variable | Description | Default |
| Device IP address on the local network. Required for device tools ( | — |
| Display size in pixels: |
|
| Directory for auto-saving preview PNG and GIF files. When unset, previews are returned in-response only. | — |
| Minimum interval between device pushes in milliseconds. Prevents device freeze from rapid-fire commands. |
|
| Transport: |
|
| HTTP server port. |
|
| HTTP session handling: |
|
| Authentication: |
|
| Log level ( |
|
| Directory for log files (Node.js only). |
|
| Storage backend: |
|
| Enable OpenTelemetry instrumentation (spans, metrics, completion logs). |
|
See .env.example for the full list of optional overrides.
Running the server
Local development
Build and run:
# One-time build bun run rebuild # Run the built server bun run start:stdio # or bun run start:httpRun checks and tests:
bun run devcheck # Lint, format, typecheck, security bun run test # Vitest test suite bun run lint:mcp # Validate MCP definitions against spec
Docker
docker build -t pixoo-mcp-server .
docker run --rm -e PIXOO_IP=192.168.1.50 -p 3010:3010 pixoo-mcp-serverThe Dockerfile defaults to HTTP transport, stateless session mode, and logs to /var/log/pixoo-mcp-server. OpenTelemetry peer dependencies are installed by default — build with --build-arg OTEL_ENABLED=false to omit them.
Project structure
Path | Purpose |
|
|
| Server-specific environment variable parsing and validation with Zod. |
| Tool definitions ( |
| Resource definitions ( |
|
|
| Pure rendering pipeline: element renderers, styled-text engine, themes, icons, effect compiler, preview encoding. No device dependency. |
| Unit and integration tests mirroring |
Development guide
See CLAUDE.md/AGENTS.md for development guidelines and architectural rules. The short version:
Handlers throw, framework catches — no
try/catchin tool logicUse
ctx.logfor request-scoped logging,ctx.statefor tenant-scoped storageThe renderer (
src/renderer/) is pure — no device dependency, testable without hardwareAll device calls go through
PixooService; everyPixooResultis checked
Contributing
Issues and pull requests are welcome. Run checks and tests before submitting:
bun run devcheck
bun run testLicense
Apache-2.0 — see LICENSE for details.
Available Tools
7 toolspixoo_compose_scenepixoo_compose_sceneAIdempotent
Compose a full scene: layered elements (text, icons, widgets, shapes, bitmaps, images, sprites) with per-element effects and keyframes, static or animated. Returns the rendered scene as an image content block for immediate inspection. Elements render back-to-front in array order. For text-only display use pixoo_display_text; run pixoo_design_brief with topic "scene" or "dashboard" for layout and palette guidance.
| Name | Required | Description | Default |
|---|---|---|---|
| push | No | Push to device (default: true). | |
| speed | No | Milliseconds per frame for animations (default: 150ms). | |
| frames | No | Number of animation frames (1–40, default: 1). 20 frames at 150ms ≈ 3s loop. | |
| output | No | Absolute, already-normalized path to save the first frame to, in addition to the PIXOO_OUTPUT_DIR auto-save when that is configured. Both paths are reported in outputFiles. | |
| elements | Yes | Scene elements rendered back-to-front. Up to 50 elements. | |
| background | Yes | Scene background: solid color, gradient, or named theme. |
Output Schema
| Name | Required | Description |
|---|---|---|
| error | No | Present when the call failed. Absent on success. |
| frames | No | Number of frames in the rendered output (1 for static, 2–40 for animations). |
| layout | No | Layout report for each element. |
| notice | No | Warning or informational notice about the render or push. |
| pushed | No | True when the device acknowledged the push. False when push: false or push failed. |
| deviceState | No | Device state after the push. Absent when push: false. |
| outputFiles | No | Absolute paths to saved output files (PNG for static, GIF for animations). Present only when PIXOO_OUTPUT_DIR is configured or output is set. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already declare idempotentHint=true and destructiveHint=false, covering the safety profile. The description adds genuine behavioral detail beyond that: it returns "the rendered scene as an image content block for immediate inspection" and discloses the z-order rule "Elements render back-to-front in array order." It does not mention the default push-to-device side effect, but this is not a contradiction and the safety info is already well covered by the annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is three sentences of pure information: the core action with capability summary, the return behavior, and the explicit routing with a requirement. Every sentence earns its place and the most important decision-relevant facts are front-loaded.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a tool with a rich nested schema, output schema, and annotations, the description does its job: it establishes the composition model, states the z-order semantics, and gives an explicit decision path to sibling tools. The main minor gap is that it does not flag that the scene is, by default, pushed to the device via the 'push' parameter, but that behavior is already fully documented in the schema itself.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The input schema covers 100% of parameters with meaningful descriptions, including the element oneOf discrimination, so the baseline is 3. The description's element type list is a useful summary but it simply reflects what the schema's oneOf already documents. It does not add meaning beyond the schema, which the rubric says the baseline already accounts for.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description starts with the specific verb-resource action "Compose a full scene" and enumerates the element families it covers (text, icons, widgets, shapes, bitmaps, images, sprites) plus the animation capabilities (effects, keyframes, static or animated). It also names the sibling tool pixoo_display_text to make the distinction explicit, so an agent can tell this tool apart without opening the schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description gives explicit routing guidance: "For text-only display use pixoo_display_text" and "run pixoo_design_brief with topic 'scene' or 'dashboard' for layout and palette guidance." This clearly handles the most confusable sibling and the companion design tool, but does not mention exclusions for the other siblings (e.g., pixoo_push_image, among others), so the guidance is strong but not exhaustive.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
pixoo_control_devicepixoo_control_deviceAIdempotent
Read or change device state: brightness (0–100), screen on/off, channel, or clock face. Call with no params to read current state only. Supply any params to apply changes before reading back state. Use pixoo_discover_devices first if PIXOO_IP is not yet configured.
| Name | Required | Description | Default |
|---|---|---|---|
| screen | No | Turn the display screen on or off. | |
| channel | No | Switch to a channel: faces, cloud, visualizer, or custom. | |
| brightness | No | Set display brightness (0–100). | |
| clockFaceId | No | Select a clock face by ID (channel must be faces). |
Output Schema
| Name | Required | Description |
|---|---|---|
| error | No | Present when the call failed. Absent on success. |
| applied | No | Settings successfully applied in this call (e.g. "brightness:80", "screen:on"). Empty when called with no params or all changes failed. A requested setting that failed is absent here and reported in a notice instead. |
| channel | No | Current channel name (faces, cloud, visualizer, or custom). Absent when device is unreachable. |
| clockId | No | Current clock face ID (faces channel only). Absent on other channels or when device is unreachable. |
| screenOn | No | True if the screen is on. Absent when device is unreachable. |
| reachable | No | True if device responded to the status read. |
| brightness | No | Current brightness level (0–100). Absent when device is unreachable. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already mark the call idempotent and non-destructive, so the description's main job is to disclose the threshold value of calling it with no params vs applying changes before reading back state. It also usefully notes that pixoo_discover_devices may be needed first. It does not provide deeper details like persistence or authentication, but none are essential here.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three sentences, no filler, operationally efficient. The first sentence defines the tool's scope, the second explains the two calling patterns, and the third handles the environment prerequisite.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For an optional padded 4-param device-control tool with fully described input fields and an existing output schema, the description covers the main workflow and prerequisite. An agent has enough to invoke it correctly: choose only params if changing state, else omit them to read state.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The schema already documents every parameter at 100% coverage, so a baseline of 3 applies. The description adds helpful usage semantics by making the interaction rule explicit: 'no params = read', 'any params = apply then read back'. That is valuable beyond the schema structure.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Description states a specific operation ('Read or change device state') and enumerates the exact dimensions affected: brightness, screen on/off, channel, and clock face. This clearly separates it from the sibling content-related tools like pixoo_display_text and pixoo_push_image.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It explicitly explains when to call with no params (read only) versus with params (apply changes), and names pixoo_discover_devices as a prerequisite when PIXOO_IP is unknown. It could slightly strengthen by naming which siblings are NOT appropriate for device-state changes, but the guidance is otherwise clear.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
pixoo_design_briefpixoo_design_briefARead-only
Return craft guidance and live device context for a design topic. Covers legibility rules, palette discipline, layout zones, animation budget, and pre-filled next-tool suggestions based on current device state. The orientation tool to run before authoring a scene, dashboard, or animation — or when troubleshooting display issues.
| Name | Required | Description | Default |
|---|---|---|---|
| topic | Yes | Design topic: text (styled text guidance), scene (composition + layout zones), dashboard (widgets + metrics), animation (motion budget + effects), pixel-art (bitmap + sprite guidance), troubleshooting (device + display issues). |
Output Schema
| Name | Required | Description |
|---|---|---|
| error | No | Present when the call failed. Absent on success. |
| topic | No | The topic that was requested. |
| craftGuidance | No | Markdown-formatted craft rules: legibility floors, palette discipline, layout zones, and technique guidance specific to the topic. |
| deviceContext | No | Live device state snapshot at the time of the request. |
| iconCategories | No | Built-in icon names grouped by category (weather, arrows, status, media). Use names in pixoo_compose_scene icon elements. |
| availableThemes | No | Available named scene themes (e.g. "midnight", "ember"). Use in background.theme or pixoo_display_text theme param. |
| nextToolSuggestions | No | Suggested next steps based on topic and device state. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The readOnlyHint annotation already tells agents this is a safe read-only operation, and the description adds useful context about what it returns: craft guidance, live device context, and next-tool suggestions. It does not disclose details about device unavailability or failure modes, but the annotation lowers the burden on the description.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is two sentences, front-loads the core purpose, and wastes no words. It covers scope, coverage areas, and a clear temporal use case without over-explaining.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a single-parameter tool with a full enum description, an output schema, and a readOnly annotation, the description is sufficiently complete. It tells agents what the tool does, what topics it covers, when to run it, and how it relates to downstream authoring tools.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The input schema fully documents the only parameter, 'topic,' including an enum and per-value descriptions. The description's mention of topics like legibility rules, palette discipline, layout zones, and animation budget reinforces the enum but does not add essential meaning beyond the schema already covers.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description uses a specific verb-resource pair ('Return craft guidance and live device context for a design topic') and clearly defines the tool's role as an orientation tool rather than an authoring tool. It distinguishes itself from sibling tools by framing it as the step to run before authoring or troubleshooting.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description explicitly states when to use the tool: before authoring a scene, dashboard, or animation, and when troubleshooting display issues. It does not explicitly name alternative tools or exclusion criteria, but the guidance is clear enough to route an agent correctly.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
pixoo_discover_devicespixoo_discover_devicesARead-only
Find Pixoo devices on the local network via Divoom's cloud discovery endpoint (requires internet — queries app.divoom-gz.com). Run once during initial setup to find device IPs; set PIXOO_IP in server configuration to enable all other tools. For ongoing device control use pixoo_control_device.
| Name | Required | Description | Default |
|---|---|---|---|
| timeoutMs | No | Discovery timeout in milliseconds (default: 5000ms). |
Output Schema
| Name | Required | Description |
|---|---|---|
| error | No | Present when the call failed. Absent on success. |
| notice | No | Recovery hint when no devices found or IP mismatch. |
| devices | No | Discovered Pixoo devices on the local network. |
| configuredIp | No | Currently configured PIXOO_IP value (absent if not set). |
| configuredIpFound | No | True if PIXOO_IP matches a discovered device; false signals an IP mismatch. Absent when PIXOO_IP is not set. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already establish readOnly and openWorld hints. The description adds value by disclosing the network dependency (requires internet) and the specific external endpoint (app.divoom-gz.com), which are important behavioral traits not captured by annotations. Does not contradict annotations.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three concise sentences, front-loaded with the primary purpose, then the setup context and routing to an alternative. Every sentence earns its place; no filler or redundancy.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Moderate-low complexity tool with an output schema, so return values need not be described. The description covers purpose, network prerequisites, and post-condition (set PIXOO_IP). Slightly more could be said about what discovery returns, but the output schema already handles that.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is 100% with clear description, default, minimum, and maximum for timeoutMs. The narrative description adds nothing about the parameter, but with full schema coverage the baseline of 3 is appropriate since no extra meaning is needed.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description uses a specific verb and resource ('Find Pixoo devices on the local network') and identifies the method via Divoom's cloud discovery endpoint. It distinguishes itself from all sibling tools, which are for display, control, or composition, by being the discovery step.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
States explicitly when to use this tool ('Run once during initial setup'), what to do with the result (set PIXOO_IP in server configuration), and names the alternative for ongoing control (pixoo_control_device). This gives an agent complete routing guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
pixoo_display_textpixoo_display_textAIdempotent
Render styled text (theme, gradient, shadow, outline, auto-fit) onto the Pixoo display and push it. Returns the rendered frame as an image content block for immediate inspection. The primary tool for text-only display — for layers, icons, widgets, or animations use pixoo_compose_scene. Run pixoo_design_brief with topic "text" first for palette and legibility guidance.
| Name | Required | Description | Default |
|---|---|---|---|
| font | No | Font variant: standard (5×7) or compact (3×5). Auto-fit will choose if omitted. | |
| push | No | Push the rendered frame to the device (default: true). | |
| text | Yes | Text to display. String or array of lines. | |
| align | No | Multi-line text alignment (default: center). | |
| style | No | Text style: palette, shadow, outline, scale. | |
| theme | No | Named scene theme — sets background gradient and default text palette. | |
| effect | No | Animation effect. auto = scroll only when text overflows. Produces a multi-frame result. | |
| position | No | Text position on the display. | |
| background | No | Background color or gradient. Overrides theme background when set. | |
| brightness | No | Set device brightness before push (0–100). Failure is a warning, not an error. |
Output Schema
| Name | Required | Description |
|---|---|---|
| error | No | Present when the call failed. Absent on success. |
| layout | No | Layout report: every fit decision the renderer made. |
| notice | No | Warning or informational message about the render or push. |
| pushed | No | True when the device acknowledged the push. |
| deviceState | No | Device state after the push. Absent when push: false. |
| outputFiles | No | Absolute paths to saved PNG preview files. Present only when PIXOO_OUTPUT_DIR is configured. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
The description goes beyond the annotations (idempotentHint true, destructiveHint false) by exposing the push-to-device side effect and the fact that the tool returns the rendered frame as an image content block for inspection. This is valuable behavioral information about what physically happens to the display and what the agent should expect in the output.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is concise, front-loaded with the primary purpose, and every sentence earns its place. It covers the core action, the output behavior, the sibling distinction, and a design brief next step all without significantly bloating.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Combined with the thorough input schema, output schema, and annotations, the description is complete enough for an agent to decide when to call this tool, what parameters to consider, what to expect from the return, and what to do for richer scenes. The shape does not have an obvious missing aspect.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
The input schema is fully documented at 100% coverage with detailed descriptions for font, text, align, style, theme, effect, position, background, brightness, and push. The tool description adds no substantive param-level semantics beyond the schema already provides. It names a few style aspects, but the schema itself explains those concepts in detail.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb ('Render'), a specific resource ('text onto the Pixoo display'), and a push-and-return behavior, ending with an image content block. It also fully differentiates itself from the available sibling ('ptixoo_compose_sceene') by declaring itself as the primary text-on-console display tool. This gives the agent enough high-level directional information before inspecting the schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description explicitly says this is the primary tool for text-only display and directs the agent to 'pixoo_compose_cene' for layers, icons, widgets, or animations. It also recommends running 'pixoo_design_brief' with topic 'text' first. That is a useful when-to-use vs. when-not-to-use instruction sequence.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
pixoo_overlay_textpixoo_overlay_textAIdempotent
Set or clear a device-native scrolling text overlay. Use mode "set" to add or update an overlay on a slot (0–19); use mode "clear" to remove it. Overlays use device-rendered fonts (115 font IDs, 0–114) and persist across channel switches until explicitly cleared with mode "clear". Not previewable — rendering happens on-device. Best for persistent tickers over pushed scenes; for styled, previewable text use pixoo_display_text instead.
| Name | Required | Description | Default |
|---|---|---|---|
| x | No | X start position on display (default: 0). | |
| y | No | Y start position on display (default: 0). | |
| id | Yes | Overlay slot ID (0–19). Each ID is an independent overlay layer. | |
| font | No | Device font ID (0–114). 0 = default, 18 = arrows, 20 = °C/°F. Device-rendered; no preview. | |
| mode | Yes | set = add/update overlay; clear = remove it. | |
| text | No | Text to display (required for mode=set). | |
| align | No | Text alignment (default: left). | left |
| color | No | Text color as CSS hex color (default: white). | #ffffff |
| speed | No | Scroll speed (0–100, device units; default: 50). | |
| width | No | Text region width in pixels (optional; defaults to display width). | |
| direction | No | Scroll direction (default: left). | left |
Output Schema
| Name | Required | Description |
|---|---|---|
| id | No | The overlay slot ID (0–19) that was operated on. |
| mode | No | Operation performed: "set" (overlay added/updated) or "clear" (overlay removed). |
| error | No | Present when the call failed. Absent on success. |
| acknowledged | No | True when the device confirmed the overlay command (error_code: 0). |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already cover idempotency and non-destructiveness, so the description correctly adds behavioral context beyond them: overlays persist across channel switches, rendering happens on-device, and the feature is not previewable. This is genuinely useful invocation-relevant detail.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is brief, front-loaded with the core action, and every sentence contributes: action, mode usage, persistence detail, limitation, and sibling routing. No filler or tautology.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given 11 parameters, 2 required, and a rich input schema, the description captures the behavioral context an agent needs: mode semantics, overlay slots, persistence, and when to choose this tool versus pixoo_display_text. No meaningful selection or invocation context is missing.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the baseline is 3; the description reinforces slot/font semantics and mode behavior, but every parameter already has a descriptive schema helper. It does not need to repeat the full parameter list.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
Description uses specific verbs ('Set or clear') and a specific resource ('device-native scrolling text overlay'), immediately distinguishing its function. It also names the sibling it is not — pixoo_display_text — so an agent can tell them apart without opening schemas.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Describes exactly when to invoke 'set' versus 'clear', and explicitly recommends pixoo_display_text for styled, previewable text. This gives clear routing guidance for the main alternative.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
pixoo_push_imagepixoo_push_imageAIdempotent
Load an image (absolute local path or https URL), resize it to fit the LED grid, and optionally push it to the display. Returns the downsampled result as an image content block so you see exactly what the display received. Nearest-neighbor kernel preserves pixel art; use lanczos3 or mitchell for photos.
| Name | Required | Description | Default |
|---|---|---|---|
| fit | No | Resize fit mode: contain (letterbox), cover (crop to fill), fill (stretch). | contain |
| push | No | Push the resized image to the device (default: true). | |
| kernel | No | Resize kernel: nearest for pixel art, lanczos3 for photos, mitchell for a balance. | nearest |
| source | Yes | Absolute local file path or https (not http) URL of the image to display. |
Output Schema
| Name | Required | Description |
|---|---|---|
| error | No | Present when the call failed. Absent on success. |
| notice | No | Warning or informational message. |
| pushed | No | True when the device acknowledged the push. |
| deviceState | No | Device state after the push. Absent when push: false. |
| outputFiles | No | Absolute paths to saved PNG preview files. Present only when PIXOO_OUTPUT_DIR is configured. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Annotations already cover idempotency (indy), open-world behavior, and not destructive. The description adds valuable behavioral detail: it returns the exact downsampled result as an image content block, and it makes explicit the resize/display pipeline. It doesn' t contradict the annotations. Slight gaps such as failure modes, but for a non-destuctive image utility this is solid.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is three compact sentences with no filler. It front-loads the main pipeline, then covers optional push behavior, return value, and kernel guidance. Every sentence earns its place.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
Given only 4 parameters, 1 required, and no output schema explain, the description is sufficient: covers input source, resizing modes, kernel choice, push default, and return-value semantics. The agent can select parameters from either schema or description, and know exactly what to expect back from the tool.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the schema already defines fit, kernel, push, and source. The description adds real selection guidance on which kernel to choose (nearest for pixel art, lanczos3/mitchell for photos), which is above the baseline and helps the agent pick semantic effective values. Small extra value beyond schema, hence a 4.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states the operation clearly: load an image from a local path or HTTPS URL, resize it to fit the LED grid, optionally push to the display, and return the downsampled result. This is a specific verb-plus-resource description. It is functionally distinct from the text/device sibling tools, though it does not explicitly name them or contrast itself with a tool like pixoo_compose_scene.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
The description outlines the pipeline and the 'optionally push' behavior, which implies a preview-only use case when push=false. It does not explicitly state when to use this tool over siblings such as pixoo_compose_scene, pixoo_overlay_text, or pixoo_control_device. There is enough context for the agent to infer, but no direct that when-to-use guidance.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections. Dates show when Glama detected each change.
7 tool updates
v1.1.1- Changed
pixoo_compose_scene10 fields changed- changed
Input schema / $schemaPrevious value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema" - added
Input schema / additionalPropertiesAdded value: +false - changed
Input schema / properties / elements / items / oneOfPrevious value: -[ - { - "description": "Styled text element rendered at a position.", - "properties": { - "animate": { - "additionalProperties": { - "description": "Keyframe entries: [[frame, value], ...].", - "items": { - "items": [ - { - "description": "Frame index.", - "type": "number" - }, - { - "anyOf": [ - { - "type": "number" - }, - { - "type": "string" - }, - { - "type": "boolean" - } - ], - "description": "Value at this frame." - } - ], - "type": "array" - }, - "type": "array" - }, - "description": "Raw keyframe animation map for this element.", - "propertyNames": { - "type": "string" - }, - "type": "object" - }, - "color": { - "description": "Flat text color.", - "type": "string" - }, - "dx": { - "description": "X offset nudge in pixels.", - "maximum": 9007199254740991, - "minimum": -9007199254740991, - "type": "integer" - }, - "dy": { - "description": "Y offset nudge in pixels.", - "maximum": 9007199254740991, - "minimum": -9007199254740991, - "type": "integer" - }, - "effect": { - "description": "Named animation preset for this element.", - "properties": { - "amplitude": { - "description": "Effect intensity: pixels of movement for float/scroll/drift, 0–1 scale for fade/pulse. Default varies by effect (float: 2px).", - "type": "number" - }, - "name": { - "description": "Named animation preset.", - "enum": [ - "float", - "scroll-left", - "scroll-right", - "pulse", - "blink", - "twinkle", - "drift", - "fade-in", - "fade-out" - ], - "type": "string" - }, - "period": { - "description": "Effect period in frames. Defaults to total scene frame count.", - "type": "number" - }, - "phase": { - "description": "Phase offset 0–1 to stagger this element relative to others (0.5 = half-cycle offset).", - "type": "number" - } - }, - "required": [ - "name" - ], - "type": "object" - }, - "font": { - "description": "Font variant.", - "enum": [ - "standard", - "compact" - ], - "type": "string" - }, - "opacity": { - "description": "Opacity 0–100 (default: 100).", - "maximum": 100, - "minimum": 0, - "type": "integer" - }, - "style": { - "description": "Text style options.", - "properties": { - "color": { - "description": "Flat color override.", - "type": "string" - }, - "outline": { - "description": "1px outline.", - "type": "boolean" - }, - "palette": { - "anyOf": [ - { - "description": "Named built-in color palette for the vertical ramp.", - "enum": [ - "ember", - "ice", - "neon", - "fire", - "lavender", - "claude", - "mono" - ], - "type": "string" - }, - { - "description": "Custom gradient stop: specify top and bottom colors directly.", - "properties": { - "from": { - "description": "Top color.", - "type": "string" - }, - "to": { - "description": "Bottom color.", - "type": "string" - } - }, - "required": [ - "from", - "to" - ], - "type": "object" - } - ], - "description": "Palette name or custom gradient stop for vertical color ramp." - }, - "scale": { - "description": "Integer pixel scale multiplier.", - "maximum": 8, - "minimum": 1, - "type": "integer" - }, - "shadow": { - "description": "Drop shadow.", - "type": "boolean" - } - }, - "type": "object" - }, - "text": { - "description": "Text content to render.", - "type": "string" - }, - "type": { - "const": "text", - "description": "Text element type.", - "type": "string" - }, - "visible": { - "description": "Whether the element is visible (default: true).", - "type": "boolean" - }, - "x": { - "anyOf": [ - { - "description": "Absolute pixel X coordinate (0 = left edge).", - "type": "number" - }, - { - "description": "Semantic horizontal alignment.", - "enum": [ - "left", - "center", - "right" - ], - "type": "string" - } - ], - "description": "X position (default: 0)." - }, - "y": { - "anyOf": [ - { - "description": "Absolute pixel Y coordinate (0 = top edge).", - "type": "number" - }, - { - "description": "Semantic vertical alignment.", - "enum": [ - "top", - "center", - "bottom" - ], - "type": "string" - } - ], - "description": "Y position (default: 0)." - } - }, - "required": [ - "type", - "text" - ], - "type": "object" - }, - { - "description": "Built-in or custom SVG icon element.", - "properties": { - "animate": { - "additionalProperties": { - "description": "Keyframe entries: [[frame, value], ...].", - "items": { - "items": [ - { - "description": "Frame index.", - "type": "number" - }, - { - "anyOf": [ - { - "type": "number" - }, - { - "type": "string" - }, - { - "type": "boolean" - } - ], - "description": "Value at this frame." - } - ], - "type": "array" - }, - "type": "array" - }, - "description": "Raw keyframe animation map for this element.", - "propertyNames": { - "type": "string" - }, - "type": "object" - }, - "color": { - "description": "Icon fill color.", - "type": "string" - }, - "d": { - "description": "Custom SVG path d attribute.", - "type": "string" - }, - "dx": { - "description": "X offset nudge in pixels.", - "maximum": 9007199254740991, - "minimum": -9007199254740991, - "type": "integer" - }, - "dy": { - "description": "Y offset nudge in pixels.", - "maximum": 9007199254740991, - "minimum": -9007199254740991, - "type": "integer" - }, - "effect": { - "description": "Named animation preset for this element.", - "properties": { - "amplitude": { - "description": "Effect intensity: pixels of movement for float/scroll/drift, 0–1 scale for fade/pulse. Default varies by effect (float: 2px).", - "type": "number" - }, - "name": { - "description": "Named animation preset.", - "enum": [ - "float", - "scroll-left", - "scroll-right", - "pulse", - "blink", - "twinkle", - "drift", - "fade-in", - "fade-out" - ], - "type": "string" - }, - "period": { - "description": "Effect period in frames. Defaults to total scene frame count.", - "type": "number" - }, - "phase": { - "description": "Phase offset 0–1 to stagger this element relative to others (0.5 = half-cycle offset).", - "type": "number" - } - }, - "required": [ - "name" - ], - "type": "object" - }, - "h": { - "description": "Render height in pixels (default: 12).", - "maximum": 9007199254740991, - "minimum": -9007199254740991, - "type": "integer" - }, - "name": { - "description": "Built-in icon name (see pixoo://reference/icons).", - "type": "string" - }, - "opacity": { - "description": "Opacity 0–100 (default: 100).", - "maximum": 100, - "minimum": 0, - "type": "integer" - }, - "palette": { - "description": "Named palette for colored icon.", - "enum": [ - "ember", - "ice", - "neon", - "fire", - "lavender", - "claude", - "mono" - ], - "type": "string" - }, - "type": { - "const": "icon", - "description": "Icon element type.", - "type": "string" - }, - "viewBox": { - "description": "SVG viewBox string (default: \"0 0 16 16\").", - "type": "string" - }, - "visible": { - "description": "Whether the element is visible (default: true).", - "type": "boolean" - }, - "w": { - "description": "Render width in pixels (default: 12).", - "maximum": 9007199254740991, - "minimum": -9007199254740991, - "type": "integer" - }, - "x": { - "anyOf": [ - { - "description": "Absolute pixel X coordinate (0 = left edge).", - "type": "number" - }, - { - "description": "Semantic horizontal alignment.", - "enum": [ - "left", - "center", - "right" - ], - "type": "string" - } - ], - "description": "X position." - }, - "y": { - "anyOf": [ - { - "description": "Absolute pixel Y coordinate (0 = top edge).", - "type": "number" - }, - { - "description": "Semantic vertical alignment.", - "enum": [ - "top", - "center", - "bottom" - ], - "type": "string" - } - ], - "description": "Y position." - } - }, - "required": [ - "type" - ], - "type": "object" - }, - { - "description": "Filled or gradient rectangle element.", - "properties": { - "animate": { - "additionalProperties": { - "description": "Keyframe entries: [[frame, value], ...].", - "items": { - "items": [ - { - "description": "Frame index.", - "type": "number" - }, - { - "anyOf": [ - { - "type": "number" - }, - { - "type": "string" - }, - { - "type": "boolean" - } - ], - "description": "Value at this frame." - } - ], - "type": "array" - }, - "type": "array" - }, - "description": "Raw keyframe animation map for this element.", - "propertyNames": { - "type": "string" - }, - "type": "object" - }, - "borderColor": { - "description": "1px border color.", - "type": "string" - }, - "color": { - "description": "Fill color.", - "type": "string" - }, - "dx": { - "description": "X offset nudge in pixels.", - "maximum": 9007199254740991, - "minimum": -9007199254740991, - "type": "integer" - }, - "dy": { - "description": "Y offset nudge in pixels.", - "maximum": 9007199254740991, - "minimum": -9007199254740991, - "type": "integer" - }, - "effect": { - "description": "Named animation preset for this element.", - "properties": { - "amplitude": { - "description": "Effect intensity: pixels of movement for float/scroll/drift, 0–1 scale for fade/pulse. Default varies by effect (float: 2px).", - "type": "number" - }, - "name": { - "description": "Named animation preset.", - "enum": [ - "float", - "scroll-left", - "scroll-right", - "pulse", - "blink", - "twinkle", - "drift", - "fade-in", - "fade-out" - ], - "type": "string" - }, - "period": { - "description": "Effect period in frames. Defaults to total scene frame count.", - "type": "number" - }, - "phase": { - "description": "Phase offset 0–1 to stagger this element relative to others (0.5 = half-cycle offset).", - "type": "number" - } - }, - "required": [ - "name" - ], - "type": "object" - }, - "gradient": { - "description": "Gradient fill.", - "properties": { - "from": { - "description": "Start color.", - "type": "string" - }, - "to": { - "description": "End color.", - "type": "string" - }, - "type": { - "description": "v = vertical, h = horizontal.", - "enum": [ - "v", - "h" - ], - "type": "string" - } - }, - "required": [ - "type", - "from", - "to" - ], - "type": "object" - }, - "h": { - "description": "Height in pixels.", - "maximum": 9007199254740991, - "minimum": 1, - "type": "integer" - }, - "opacity": { - "description": "Opacity 0–100 (default: 100).", - "maximum": 100, - "minimum": 0, - "type": "integer" - }, - "type": { - "const": "rect", - "description": "Rectangle element type.", - "type": "string" - }, - "visible": { - "description": "Whether the element is visible (default: true).", - "type": "boolean" - }, - "w": { - "description": "Width in pixels.", - "maximum": 9007199254740991, - "minimum": 1, - "type": "integer" - }, - "x": { - "description": "X coordinate.", - "maximum": 9007199254740991, - "minimum": -9007199254740991, - "type": "integer" - }, - "y": { - "description": "Y coordinate.", - "maximum": 9007199254740991, - "minimum": -9007199254740991, - "type": "integer" - } - }, - "required": [ - "type", - "x", - "y", - "w", - "h" - ], - "type": "object" - }, - { - "description": "Filled or outline circle element.", - "properties": { - "animate": { - "additionalProperties": { - "description": "Keyframe entries: [[frame, value], ...].", - "items": { - "items": [ - { - "description": "Frame index.", - "type": "number" - }, - { - "anyOf": [ - { - "type": "number" - }, - { - "type": "string" - }, - { - "type": "boolean" - } - ], - "description": "Value at this frame." - } - ], - "type": "array" - }, - "type": "array" - }, - "description": "Raw keyframe animation map for this element.", - "propertyNames": { - "type": "string" - }, - "type": "object" - }, - "color": { - "description": "Circle color.", - "type": "string" - }, - "cx": { - "description": "Center X coordinate.", - "maximum": 9007199254740991, - "minimum": -9007199254740991, - "type": "integer" - }, - "cy": { - "description": "Center Y coordinate.", - "maximum": 9007199254740991, - "minimum": -9007199254740991, - "type": "integer" - }, - "dx": { - "description": "X offset nudge in pixels.", - "maximum": 9007199254740991, - "minimum": -9007199254740991, - "type": "integer" - }, - "dy": { - "description": "Y offset nudge in pixels.", - "maximum": 9007199254740991, - "minimum": -9007199254740991, - "type": "integer" - }, - "effect": { - "description": "Named animation preset for this element.", - "properties": { - "amplitude": { - "description": "Effect intensity: pixels of movement for float/scroll/drift, 0–1 scale for fade/pulse. Default varies by effect (float: 2px).", - "type": "number" - }, - "name": { - "description": "Named animation preset.", - "enum": [ - "float", - "scroll-left", - "scroll-right", - "pulse", - "blink", - "twinkle", - "drift", - "fade-in", - "fade-out" - ], - "type": "string" - }, - "period": { - "description": "Effect period in frames. Defaults to total scene frame count.", - "type": "number" - }, - "phase": { - "description": "Phase offset 0–1 to stagger this element relative to others (0.5 = half-cycle offset).", - "type": "number" - } - }, - "required": [ - "name" - ], - "type": "object" - }, - "fill": { - "description": "Filled or outline only (default: true).", - "type": "boolean" - }, - "opacity": { - "description": "Opacity 0–100 (default: 100).", - "maximum": 100, - "minimum": 0, - "type": "integer" - }, - "radius": { - "description": "Radius in pixels.", - "maximum": 9007199254740991, - "minimum": 1, - "type": "integer" - }, - "type": { - "const": "circle", - "description": "Circle element type.", - "type": "string" - }, - "visible": { - "description": "Whether the element is visible (default: true).", - "type": "boolean" - } - }, - "required": [ - "type", - "cx", - "cy", - "radius" - ], - "type": "object" - }, - { - "description": "Single-pixel line element.", - "properties": { - "animate": { - "additionalProperties": { - "description": "Keyframe entries: [[frame, value], ...].", - "items": { - "items": [ - { - "description": "Frame index.", - "type": "number" - }, - { - "anyOf": [ - { - "type": "number" - }, - { - "type": "string" - }, - { - "type": "boolean" - } - ], - "description": "Value at this frame." - } - ], - "type": "array" - }, - "type": "array" - }, - "description": "Raw keyframe animation map for this element.", - "propertyNames": { - "type": "string" - }, - "type": "object" - }, - "color": { - "description": "Line color.", - "type": "string" - }, - "dx": { - "description": "X offset nudge in pixels.", - "maximum": 9007199254740991, - "minimum": -9007199254740991, - "type": "integer" - }, - "dy": { - "description": "Y offset nudge in pixels.", - "maximum": 9007199254740991, - "minimum": -9007199254740991, - "type": "integer" - }, - "effect": { - "description": "Named animation preset for this element.", - "properties": { - "amplitude": { - "description": "Effect intensity: pixels of movement for float/scroll/drift, 0–1 scale for fade/pulse. Default varies by effect (float: 2px).", - "type": "number" - }, - "name": { - "description": "Named animation preset.", - "enum": [ - "float", - "scroll-left", - "scroll-right", - "pulse", - "blink", - "twinkle", - "drift", - "fade-in", - "fade-out" - ], - "type": "string" - }, - "period": { - "description": "Effect period in frames. Defaults to total scene frame count.", - "type": "number" - }, - "phase": { - "description": "Phase offset 0–1 to stagger this element relative to others (0.5 = half-cycle offset).", - "type": "number" - } - }, - "required": [ - "name" - ], - "type": "object" - }, - "opacity": { - "description": "Opacity 0–100 (default: 100).", - "maximum": 100, - "minimum": 0, - "type": "integer" - }, - "type": { - "const": "line", - "description": "Line element type.", - "type": "string" - }, - "visible": { - "description": "Whether the element is visible (default: true).", - "type": "boolean" - }, - "x0": { - "description": "Start X coordinate.", - "maximum": 9007199254740991, - "minimum": -9007199254740991, - "type": "integer" - }, - "x1": { - "description": "End X coordinate.", - "maximum": 9007199254740991, - "minimum": -9007199254740991, - "type": "integer" - }, - "y0": { - "description": "Start Y coordinate.", - "maximum": 9007199254740991, - "minimum": -9007199254740991, - "type": "integer" - }, - "y1": { - "description": "End Y coordinate.", - "maximum": 9007199254740991, - "minimum": -9007199254740991, - "type": "integer" - } - }, - "required": [ - "type", - "x0", - "y0", - "x1", - "y1" - ], - "type": "object" - }, - { - "description": "Horizontal progress bar widget.", - "properties": { - "animate": { - "additionalProperties": { - "description": "Keyframe entries: [[frame, value], ...].", - "items": { - "items": [ - { - "description": "Frame index.", - "type": "number" - }, - { - "anyOf": [ - { - "type": "number" - }, - { - "type": "string" - }, - { - "type": "boolean" - } - ], - "description": "Value at this frame." - } - ], - "type": "array" - }, - "type": "array" - }, - "description": "Raw keyframe animation map for this element.", - "propertyNames": { - "type": "string" - }, - "type": "object" - }, - "dx": { - "description": "X offset nudge in pixels.", - "maximum": 9007199254740991, - "minimum": -9007199254740991, - "type": "integer" - }, - "dy": { - "description": "Y offset nudge in pixels.", - "maximum": 9007199254740991, - "minimum": -9007199254740991, - "type": "integer" - }, - "effect": { - "description": "Named animation preset for this element.", - "properties": { - "amplitude": { - "description": "Effect intensity: pixels of movement for float/scroll/drift, 0–1 scale for fade/pulse. Default varies by effect (float: 2px).", - "type": "number" - }, - "name": { - "description": "Named animation preset.", - "enum": [ - "float", - "scroll-left", - "scroll-right", - "pulse", - "blink", - "twinkle", - "drift", - "fade-in", - "fade-out" - ], - "type": "string" - }, - "period": { - "description": "Effect period in frames. Defaults to total scene frame count.", - "type": "number" - }, - "phase": { - "description": "Phase offset 0–1 to stagger this element relative to others (0.5 = half-cycle offset).", - "type": "number" - } - }, - "required": [ - "name" - ], - "type": "object" - }, - "h": { - "description": "Height in pixels.", - "maximum": 9007199254740991, - "minimum": 1, - "type": "integer" - }, - "label": { - "description": "Optional label rendered on the bar.", - "type": "string" - }, - "max": { - "description": "Maximum value.", - "type": "number" - }, - "opacity": { - "description": "Opacity 0–100 (default: 100).", - "maximum": 100, - "minimum": 0, - "type": "integer" - }, - "palette": { - "description": "Named palette for gradient fill.", - "enum": [ - "ember", - "ice", - "neon", - "fire", - "lavender", - "claude", - "mono" - ], - "type": "string" - }, - "trackColor": { - "description": "Background track color.", - "type": "string" - }, - "type": { - "const": "progress", - "description": "Progress bar widget type.", - "type": "string" - }, - "value": { - "description": "Current value.", - "type": "number" - }, - "visible": { - "description": "Whether the element is visible (default: true).", - "type": "boolean" - }, - "w": { - "description": "Width in pixels.", - "maximum": 9007199254740991, - "minimum": 1, - "type": "integer" - }, - "x": { - "description": "X coordinate.", - "maximum": 9007199254740991, - "minimum": -9007199254740991, - "type": "integer" - }, - "y": { - "description": "Y coordinate.", - "maximum": 9007199254740991, - "minimum": -9007199254740991, - "type": "integer" - } - }, - "required": [ - "type", - "x", - "y", - "w", - "h", - "value", - "max" - ], - "type": "object" - }, - { - "description": "Sparkline chart widget (line or bar).", - "properties": { - "animate": { - "additionalProperties": { - "description": "Keyframe entries: [[frame, value], ...].", - "items": { - "items": [ - { - "description": "Frame index.", - "type": "number" - }, - { - "anyOf": [ - { - "type": "number" - }, - { - "type": "string" - }, - { - "type": "boolean" - } - ], - "description": "Value at this frame." - } - ], - "type": "array" - }, - "type": "array" - }, - "description": "Raw keyframe animation map for this element.", - "propertyNames": { - "type": "string" - }, - "type": "object" - }, - "color": { - "description": "Line or bar color.", - "type": "string" - }, - "data": { - "description": "Data points for the chart.", - "items": { - "type": "number" - }, - "minItems": 2, - "type": "array" - }, - "dx": { - "description": "X offset nudge in pixels.", - "maximum": 9007199254740991, - "minimum": -9007199254740991, - "type": "integer" - }, - "dy": { - "description": "Y offset nudge in pixels.", - "maximum": 9007199254740991, - "minimum": -9007199254740991, - "type": "integer" - }, - "effect": { - "description": "Named animation preset for this element.", - "properties": { - "amplitude": { - "description": "Effect intensity: pixels of movement for float/scroll/drift, 0–1 scale for fade/pulse. Default varies by effect (float: 2px).", - "type": "number" - }, - "name": { - "description": "Named animation preset.", - "enum": [ - "float", - "scroll-left", - "scroll-right", - "pulse", - "blink", - "twinkle", - "drift", - "fade-in", - "fade-out" - ], - "type": "string" - }, - "period": { - "description": "Effect period in frames. Defaults to total scene frame count.", - "type": "number" - }, - "phase": { - "description": "Phase offset 0–1 to stagger this element relative to others (0.5 = half-cycle offset).", - "type": "number" - } - }, - "required": [ - "name" - ], - "type": "object" - }, - "h": { - "description": "Height in pixels.", - "maximum": 9007199254740991, - "minimum": 2, - "type": "integer" - }, - "kind": { - "description": "Chart style (default: line).", - "enum": [ - "line", - "bar" - ], - "type": "string" - }, - "opacity": { - "description": "Opacity 0–100 (default: 100).", - "maximum": 100, - "minimum": 0, - "type": "integer" - }, - "type": { - "const": "sparkline", - "description": "Sparkline chart widget type.", - "type": "string" - }, - "visible": { - "description": "Whether the element is visible (default: true).", - "type": "boolean" - }, - "w": { - "description": "Width in pixels.", - "maximum": 9007199254740991, - "minimum": 2, - "type": "integer" - }, - "x": { - "description": "X coordinate.", - "maximum": 9007199254740991, - "minimum": -9007199254740991, - "type": "integer" - }, - "y": { - "description": "Y coordinate.", - "maximum": 9007199254740991, - "minimum": -9007199254740991, - "type": "integer" - } - }, - "required": [ - "type", - "x", - "y", - "w", - "h", - "data" - ], - "type": "object" - }, - { - "description": "Bitmap element using explicit palette indices.", - "properties": { - "animate": { - "additionalProperties": { - "description": "Keyframe entries: [[frame, value], ...].", - "items": { - "items": [ - { - "description": "Frame index.", - "type": "number" - }, - { - "anyOf": [ - { - "type": "number" - }, - { - "type": "string" - }, - { - "type": "boolean" - } - ], - "description": "Value at this frame." - } - ], - "type": "array" - }, - "type": "array" - }, - "description": "Raw keyframe animation map for this element.", - "propertyNames": { - "type": "string" - }, - "type": "object" - }, - "dx": { - "description": "X offset nudge in pixels.", - "maximum": 9007199254740991, - "minimum": -9007199254740991, - "type": "integer" - }, - "dy": { - "description": "Y offset nudge in pixels.", - "maximum": 9007199254740991, - "minimum": -9007199254740991, - "type": "integer" - }, - "effect": { - "description": "Named animation preset for this element.", - "properties": { - "amplitude": { - "description": "Effect intensity: pixels of movement for float/scroll/drift, 0–1 scale for fade/pulse. Default varies by effect (float: 2px).", - "type": "number" - }, - "name": { - "description": "Named animation preset.", - "enum": [ - "float", - "scroll-left", - "scroll-right", - "pulse", - "blink", - "twinkle", - "drift", - "fade-in", - "fade-out" - ], - "type": "string" - }, - "period": { - "description": "Effect period in frames. Defaults to total scene frame count.", - "type": "number" - }, - "phase": { - "description": "Phase offset 0–1 to stagger this element relative to others (0.5 = half-cycle offset).", - "type": "number" - } - }, - "required": [ - "name" - ], - "type": "object" - }, - "opacity": { - "description": "Opacity 0–100 (default: 100).", - "maximum": 100, - "minimum": 0, - "type": "integer" - }, - "palette": { - "description": "Color palette: array of hex colors indexed 0-F.", - "items": { - "type": "string" - }, - "type": "array" - }, - "rows": { - "description": "Row strings of hex palette indices (0-F, space = transparent).", - "items": { - "type": "string" - }, - "type": "array" - }, - "type": { - "const": "bitmap", - "description": "Bitmap element using palette indices.", - "type": "string" - }, - "visible": { - "description": "Whether the element is visible (default: true).", - "type": "boolean" - }, - "x": { - "description": "X coordinate.", - "maximum": 9007199254740991, - "minimum": -9007199254740991, - "type": "integer" - }, - "y": { - "description": "Y coordinate.", - "maximum": 9007199254740991, - "minimum": -9007199254740991, - "type": "integer" - } - }, - "required": [ - "type", - "x", - "y", - "rows", - "palette" - ], - "type": "object" - }, - { - "description": "Sparse scatter of individually colored pixels.", - "properties": { - "animate": { - "additionalProperties": { - "description": "Keyframe entries: [[frame, value], ...].", - "items": { - "items": [ - { - "description": "Frame index.", - "type": "number" - }, - { - "anyOf": [ - { - "type": "number" - }, - { - "type": "string" - }, - { - "type": "boolean" - } - ], - "description": "Value at this frame." - } - ], - "type": "array" - }, - "type": "array" - }, - "description": "Raw keyframe animation map for this element.", - "propertyNames": { - "type": "string" - }, - "type": "object" - }, - "data": { - "description": "Array of pixel positions and colors.", - "items": { - "description": "A single pixel: position and color.", - "properties": { - "color": { - "description": "Pixel color.", - "type": "string" - }, - "x": { - "description": "Pixel X coordinate.", - "maximum": 9007199254740991, - "minimum": -9007199254740991, - "type": "integer" - }, - "y": { - "description": "Pixel Y coordinate.", - "maximum": 9007199254740991, - "minimum": -9007199254740991, - "type": "integer" - } - }, - "required": [ - "x", - "y", - "color" - ], - "type": "object" - }, - "type": "array" - }, - "dx": { - "description": "X offset nudge in pixels.", - "maximum": 9007199254740991, - "minimum": -9007199254740991, - "type": "integer" - }, - "dy": { - "description": "Y offset nudge in pixels.", - "maximum": 9007199254740991, - "minimum": -9007199254740991, - "type": "integer" - }, - "effect": { - "description": "Named animation preset for this element.", - "properties": { - "amplitude": { - "description": "Effect intensity: pixels of movement for float/scroll/drift, 0–1 scale for fade/pulse. Default varies by effect (float: 2px).", - "type": "number" - }, - "name": { - "description": "Named animation preset.", - "enum": [ - "float", - "scroll-left", - "scroll-right", - "pulse", - "blink", - "twinkle", - "drift", - "fade-in", - "fade-out" - ], - "type": "string" - }, - "period": { - "description": "Effect period in frames. Defaults to total scene frame count.", - "type": "number" - }, - "phase": { - "description": "Phase offset 0–1 to stagger this element relative to others (0.5 = half-cycle offset).", - "type": "number" - } - }, - "required": [ - "name" - ], - "type": "object" - }, - "opacity": { - "description": "Opacity 0–100 (default: 100).", - "maximum": 100, - "minimum": 0, - "type": "integer" - }, - "type": { - "const": "pixels", - "description": "Sparse pixel dots element.", - "type": "string" - }, - "visible": { - "description": "Whether the element is visible (default: true).", - "type": "boolean" - } - }, - "required": [ - "type", - "data" - ], - "type": "object" - }, - { - "description": "External image element (local path or https URL).", - "properties": { - "animate": { - "additionalProperties": { - "description": "Keyframe entries: [[frame, value], ...].", - "items": { - "items": [ - { - "description": "Frame index.", - "type": "number" - }, - { - "anyOf": [ - { - "type": "number" - }, - { - "type": "string" - }, - { - "type": "boolean" - } - ], - "description": "Value at this frame." - } - ], - "type": "array" - }, - "type": "array" - }, - "description": "Raw keyframe animation map for this element.", - "propertyNames": { - "type": "string" - }, - "type": "object" - }, - "dx": { - "description": "X offset nudge in pixels.", - "maximum": 9007199254740991, - "minimum": -9007199254740991, - "type": "integer" - }, - "dy": { - "description": "Y offset nudge in pixels.", - "maximum": 9007199254740991, - "minimum": -9007199254740991, - "type": "integer" - }, - "effect": { - "description": "Named animation preset for this element.", - "properties": { - "amplitude": { - "description": "Effect intensity: pixels of movement for float/scroll/drift, 0–1 scale for fade/pulse. Default varies by effect (float: 2px).", - "type": "number" - }, - "name": { - "description": "Named animation preset.", - "enum": [ - "float", - "scroll-left", - "scroll-right", - "pulse", - "blink", - "twinkle", - "drift", - "fade-in", - "fade-out" - ], - "type": "string" - }, - "period": { - "description": "Effect period in frames. Defaults to total scene frame count.", - "type": "number" - }, - "phase": { - "description": "Phase offset 0–1 to stagger this element relative to others (0.5 = half-cycle offset).", - "type": "number" - } - }, - "required": [ - "name" - ], - "type": "object" - }, - "fit": { - "description": "Resize fit mode (default: contain).", - "enum": [ - "contain", - "cover", - "fill" - ], - "type": "string" - }, - "h": { - "description": "Target height (default: canvas height).", - "maximum": 9007199254740991, - "minimum": -9007199254740991, - "type": "integer" - }, - "kernel": { - "description": "Resize kernel: nearest for pixel art, lanczos3 for photos (default: nearest).", - "enum": [ - "nearest", - "lanczos3", - "mitchell" - ], - "type": "string" - }, - "opacity": { - "description": "Opacity 0–100 (default: 100).", - "maximum": 100, - "minimum": 0, - "type": "integer" - }, - "source": { - "description": "Absolute local file path or https (not http) URL.", - "type": "string" - }, - "type": { - "const": "image", - "description": "Image element (local path or https URL).", - "type": "string" - }, - "visible": { - "description": "Whether the element is visible (default: true).", - "type": "boolean" - }, - "w": { - "description": "Target width (default: canvas width).", - "maximum": 9007199254740991, - "minimum": -9007199254740991, - "type": "integer" - }, - "x": { - "description": "X offset on canvas (default: 0).", - "maximum": 9007199254740991, - "minimum": -9007199254740991, - "type": "integer" - }, - "y": { - "description": "Y offset on canvas (default: 0).", - "maximum": 9007199254740991, - "minimum": -9007199254740991, - "type": "integer" - } - }, - "required": [ - "type", - "source" - ], - "type": "object" - }, - { - "description": "Sprite sheet element for character or tile art.", - "properties": { - "animate": { - "additionalProperties": { - "description": "Keyframe entries: [[frame, value], ...].", - "items": { - "items": [ - { - "description": "Frame index.", - "type": "number" - }, - { - "anyOf": [ - { - "type": "number" - }, - { - "type": "string" - }, - { - "type": "boolean" - } - ], - "description": "Value at this frame." - } - ], - "type": "array" - }, - "type": "array" - }, - "description": "Raw keyframe animation map for this element.", - "propertyNames": { - "type": "string" - }, - "type": "object" - }, - "bodyColor": { - "description": "Override body color.", - "type": "string" - }, - "cols": { - "description": "Number of columns in the sprite grid.", - "maximum": 9007199254740991, - "minimum": 1, - "type": "integer" - }, - "darkColor": { - "description": "Override dark/eye color.", - "type": "string" - }, - "dx": { - "description": "X offset nudge in pixels.", - "maximum": 9007199254740991, - "minimum": -9007199254740991, - "type": "integer" - }, - "dy": { - "description": "Y offset nudge in pixels.", - "maximum": 9007199254740991, - "minimum": -9007199254740991, - "type": "integer" - }, - "effect": { - "description": "Named animation preset for this element.", - "properties": { - "amplitude": { - "description": "Effect intensity: pixels of movement for float/scroll/drift, 0–1 scale for fade/pulse. Default varies by effect (float: 2px).", - "type": "number" - }, - "name": { - "description": "Named animation preset.", - "enum": [ - "float", - "scroll-left", - "scroll-right", - "pulse", - "blink", - "twinkle", - "drift", - "fade-in", - "fade-out" - ], - "type": "string" - }, - "period": { - "description": "Effect period in frames. Defaults to total scene frame count.", - "type": "number" - }, - "phase": { - "description": "Phase offset 0–1 to stagger this element relative to others (0.5 = half-cycle offset).", - "type": "number" - } - }, - "required": [ - "name" - ], - "type": "object" - }, - "opacity": { - "description": "Opacity 0–100 (default: 100).", - "maximum": 100, - "minimum": 0, - "type": "integer" - }, - "path": { - "description": "Absolute local path to the sprite sheet image.", - "type": "string" - }, - "rows": { - "description": "Number of rows in the sprite grid.", - "maximum": 9007199254740991, - "minimum": 1, - "type": "integer" - }, - "scale": { - "description": "Pixel scale factor.", - "maximum": 9007199254740991, - "minimum": 1, - "type": "integer" - }, - "type": { - "const": "sprite", - "description": "Sprite sheet element.", - "type": "string" - }, - "visible": { - "description": "Whether the element is visible (default: true).", - "type": "boolean" - }, - "x": { - "anyOf": [ - { - "description": "Absolute pixel X coordinate (0 = left edge).", - "type": "number" - }, - { - "description": "Semantic horizontal alignment.", - "enum": [ - "left", - "center", - "right" - ], - "type": "string" - } - ], - "description": "X position (default: center)." - }, - "y": { - "description": "Y position (default: 0).", - "maximum": 9007199254740991, - "minimum": -9007199254740991, - "type": "integer" - } - }, - "required": [ - "type", - "path", - "cols", - "rows" - ], - "type": "object" - } -]New value: +[ + { + "description": "Styled text element rendered at a position.", + "properties": { + "animate": { + "additionalProperties": { + "description": "Keyframe entries: [[frame, value], ...].", + "items": { + "prefixItems": [ + { + "description": "Frame index.", + "type": "number" + }, + { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string" + }, + { + "type": "boolean" + } + ], + "description": "Value at this frame." + } + ], + "type": "array" + }, + "type": "array" + }, + "description": "Raw keyframe animation map for this element.", + "propertyNames": { + "type": "string" + }, + "type": "object" + }, + "color": { + "description": "Flat text color.", + "type": "string" + }, + "dx": { + "description": "X offset nudge in pixels.", + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + "dy": { + "description": "Y offset nudge in pixels.", + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + "effect": { + "description": "Named animation preset for this element.", + "properties": { + "amplitude": { + "description": "Effect intensity: pixels of movement for float/scroll/drift, 0–1 scale for fade/pulse. Default varies by effect (float: 2px).", + "type": "number" + }, + "name": { + "description": "Named animation preset.", + "enum": [ + "float", + "scroll-left", + "scroll-right", + "pulse", + "blink", + "twinkle", + "drift", + "fade-in", + "fade-out" + ], + "type": "string" + }, + "period": { + "description": "Effect period in frames. Defaults to total scene frame count.", + "type": "number" + }, + "phase": { + "description": "Phase offset 0–1 to stagger this element relative to others (0.5 = half-cycle offset).", + "type": "number" + } + }, + "required": [ + "name" + ], + "type": "object" + }, + "font": { + "description": "Font variant.", + "enum": [ + "standard", + "compact" + ], + "type": "string" + }, + "opacity": { + "description": "Opacity 0–100 (default: 100).", + "maximum": 100, + "minimum": 0, + "type": "integer" + }, + "style": { + "description": "Text style options.", + "properties": { + "color": { + "description": "Flat color override.", + "type": "string" + }, + "outline": { + "description": "1px outline.", + "type": "boolean" + }, + "palette": { + "anyOf": [ + { + "description": "Named built-in color palette for the vertical ramp.", + "enum": [ + "ember", + "ice", + "neon", + "fire", + "lavender", + "claude", + "mono" + ], + "type": "string" + }, + { + "description": "Custom gradient stop: specify top and bottom colors directly.", + "properties": { + "from": { + "description": "Top color.", + "type": "string" + }, + "to": { + "description": "Bottom color.", + "type": "string" + } + }, + "required": [ + "from", + "to" + ], + "type": "object" + } + ], + "description": "Palette name or custom gradient stop for vertical color ramp." + }, + "scale": { + "description": "Integer pixel scale multiplier.", + "maximum": 8, + "minimum": 1, + "type": "integer" + }, + "shadow": { + "description": "Drop shadow.", + "type": "boolean" + } + }, + "type": "object" + }, + "text": { + "description": "Text content to render.", + "type": "string" + }, + "type": { + "const": "text", + "description": "Text element type.", + "type": "string" + }, + "visible": { + "description": "Whether the element is visible (default: true).", + "type": "boolean" + }, + "x": { + "anyOf": [ + { + "description": "Absolute pixel X coordinate (0 = left edge).", + "type": "number" + }, + { + "description": "Semantic horizontal alignment.", + "enum": [ + "left", + "center", + "right" + ], + "type": "string" + } + ], + "description": "X position (default: 0)." + }, + "y": { + "anyOf": [ + { + "description": "Absolute pixel Y coordinate (0 = top edge).", + "type": "number" + }, + { + "description": "Semantic vertical alignment.", + "enum": [ + "top", + "center", + "bottom" + ], + "type": "string" + } + ], + "description": "Y position (default: 0)." + } + }, + "required": [ + "type", + "text" + ], + "type": "object" + }, + { + "description": "Built-in or custom SVG icon element.", + "properties": { + "animate": { + "additionalProperties": { + "description": "Keyframe entries: [[frame, value], ...].", + "items": { + "prefixItems": [ + { + "description": "Frame index.", + "type": "number" + }, + { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string" + }, + { + "type": "boolean" + } + ], + "description": "Value at this frame." + } + ], + "type": "array" + }, + "type": "array" + }, + "description": "Raw keyframe animation map for this element.", + "propertyNames": { + "type": "string" + }, + "type": "object" + }, + "color": { + "description": "Icon fill color.", + "type": "string" + }, + "d": { + "description": "Custom SVG path d attribute.", + "type": "string" + }, + "dx": { + "description": "X offset nudge in pixels.", + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + "dy": { + "description": "Y offset nudge in pixels.", + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + "effect": { + "description": "Named animation preset for this element.", + "properties": { + "amplitude": { + "description": "Effect intensity: pixels of movement for float/scroll/drift, 0–1 scale for fade/pulse. Default varies by effect (float: 2px).", + "type": "number" + }, + "name": { + "description": "Named animation preset.", + "enum": [ + "float", + "scroll-left", + "scroll-right", + "pulse", + "blink", + "twinkle", + "drift", + "fade-in", + "fade-out" + ], + "type": "string" + }, + "period": { + "description": "Effect period in frames. Defaults to total scene frame count.", + "type": "number" + }, + "phase": { + "description": "Phase offset 0–1 to stagger this element relative to others (0.5 = half-cycle offset).", + "type": "number" + } + }, + "required": [ + "name" + ], + "type": "object" + }, + "h": { + "description": "Render height in pixels (default: 12).", + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + "name": { + "description": "Built-in icon name (see pixoo://reference/icons).", + "type": "string" + }, + "opacity": { + "description": "Opacity 0–100 (default: 100).", + "maximum": 100, + "minimum": 0, + "type": "integer" + }, + "palette": { + "description": "Named palette for colored icon.", + "enum": [ + "ember", + "ice", + "neon", + "fire", + "lavender", + "claude", + "mono" + ], + "type": "string" + }, + "type": { + "const": "icon", + "description": "Icon element type.", + "type": "string" + }, + "viewBox": { + "description": "SVG viewBox string (default: \"0 0 16 16\").", + "type": "string" + }, + "visible": { + "description": "Whether the element is visible (default: true).", + "type": "boolean" + }, + "w": { + "description": "Render width in pixels (default: 12).", + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + "x": { + "anyOf": [ + { + "description": "Absolute pixel X coordinate (0 = left edge).", + "type": "number" + }, + { + "description": "Semantic horizontal alignment.", + "enum": [ + "left", + "center", + "right" + ], + "type": "string" + } + ], + "description": "X position." + }, + "y": { + "anyOf": [ + { + "description": "Absolute pixel Y coordinate (0 = top edge).", + "type": "number" + }, + { + "description": "Semantic vertical alignment.", + "enum": [ + "top", + "center", + "bottom" + ], + "type": "string" + } + ], + "description": "Y position." + } + }, + "required": [ + "type" + ], + "type": "object" + }, + { + "description": "Filled or gradient rectangle element.", + "properties": { + "animate": { + "additionalProperties": { + "description": "Keyframe entries: [[frame, value], ...].", + "items": { + "prefixItems": [ + { + "description": "Frame index.", + "type": "number" + }, + { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string" + }, + { + "type": "boolean" + } + ], + "description": "Value at this frame." + } + ], + "type": "array" + }, + "type": "array" + }, + "description": "Raw keyframe animation map for this element.", + "propertyNames": { + "type": "string" + }, + "type": "object" + }, + "borderColor": { + "description": "1px border color.", + "type": "string" + }, + "color": { + "description": "Fill color.", + "type": "string" + }, + "dx": { + "description": "X offset nudge in pixels.", + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + "dy": { + "description": "Y offset nudge in pixels.", + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + "effect": { + "description": "Named animation preset for this element.", + "properties": { + "amplitude": { + "description": "Effect intensity: pixels of movement for float/scroll/drift, 0–1 scale for fade/pulse. Default varies by effect (float: 2px).", + "type": "number" + }, + "name": { + "description": "Named animation preset.", + "enum": [ + "float", + "scroll-left", + "scroll-right", + "pulse", + "blink", + "twinkle", + "drift", + "fade-in", + "fade-out" + ], + "type": "string" + }, + "period": { + "description": "Effect period in frames. Defaults to total scene frame count.", + "type": "number" + }, + "phase": { + "description": "Phase offset 0–1 to stagger this element relative to others (0.5 = half-cycle offset).", + "type": "number" + } + }, + "required": [ + "name" + ], + "type": "object" + }, + "gradient": { + "description": "Gradient fill.", + "properties": { + "from": { + "description": "Start color.", + "type": "string" + }, + "to": { + "description": "End color.", + "type": "string" + }, + "type": { + "description": "v = vertical, h = horizontal.", + "enum": [ + "v", + "h" + ], + "type": "string" + } + }, + "required": [ + "type", + "from", + "to" + ], + "type": "object" + }, + "h": { + "description": "Height in pixels.", + "maximum": 9007199254740991, + "minimum": 1, + "type": "integer" + }, + "opacity": { + "description": "Opacity 0–100 (default: 100).", + "maximum": 100, + "minimum": 0, + "type": "integer" + }, + "type": { + "const": "rect", + "description": "Rectangle element type.", + "type": "string" + }, + "visible": { + "description": "Whether the element is visible (default: true).", + "type": "boolean" + }, + "w": { + "description": "Width in pixels.", + "maximum": 9007199254740991, + "minimum": 1, + "type": "integer" + }, + "x": { + "description": "X coordinate.", + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + "y": { + "description": "Y coordinate.", + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + } + }, + "required": [ + "type", + "x", + "y", + "w", + "h" + ], + "type": "object" + }, + { + "description": "Filled or outline circle element.", + "properties": { + "animate": { + "additionalProperties": { + "description": "Keyframe entries: [[frame, value], ...].", + "items": { + "prefixItems": [ + { + "description": "Frame index.", + "type": "number" + }, + { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string" + }, + { + "type": "boolean" + } + ], + "description": "Value at this frame." + } + ], + "type": "array" + }, + "type": "array" + }, + "description": "Raw keyframe animation map for this element.", + "propertyNames": { + "type": "string" + }, + "type": "object" + }, + "color": { + "description": "Circle color.", + "type": "string" + }, + "cx": { + "description": "Center X coordinate.", + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + "cy": { + "description": "Center Y coordinate.", + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + "dx": { + "description": "X offset nudge in pixels.", + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + "dy": { + "description": "Y offset nudge in pixels.", + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + "effect": { + "description": "Named animation preset for this element.", + "properties": { + "amplitude": { + "description": "Effect intensity: pixels of movement for float/scroll/drift, 0–1 scale for fade/pulse. Default varies by effect (float: 2px).", + "type": "number" + }, + "name": { + "description": "Named animation preset.", + "enum": [ + "float", + "scroll-left", + "scroll-right", + "pulse", + "blink", + "twinkle", + "drift", + "fade-in", + "fade-out" + ], + "type": "string" + }, + "period": { + "description": "Effect period in frames. Defaults to total scene frame count.", + "type": "number" + }, + "phase": { + "description": "Phase offset 0–1 to stagger this element relative to others (0.5 = half-cycle offset).", + "type": "number" + } + }, + "required": [ + "name" + ], + "type": "object" + }, + "fill": { + "description": "Filled or outline only (default: true).", + "type": "boolean" + }, + "opacity": { + "description": "Opacity 0–100 (default: 100).", + "maximum": 100, + "minimum": 0, + "type": "integer" + }, + "radius": { + "description": "Radius in pixels.", + "maximum": 9007199254740991, + "minimum": 1, + "type": "integer" + }, + "type": { + "const": "circle", + "description": "Circle element type.", + "type": "string" + }, + "visible": { + "description": "Whether the element is visible (default: true).", + "type": "boolean" + } + }, + "required": [ + "type", + "cx", + "cy", + "radius" + ], + "type": "object" + }, + { + "description": "Single-pixel line element.", + "properties": { + "animate": { + "additionalProperties": { + "description": "Keyframe entries: [[frame, value], ...].", + "items": { + "prefixItems": [ + { + "description": "Frame index.", + "type": "number" + }, + { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string" + }, + { + "type": "boolean" + } + ], + "description": "Value at this frame." + } + ], + "type": "array" + }, + "type": "array" + }, + "description": "Raw keyframe animation map for this element.", + "propertyNames": { + "type": "string" + }, + "type": "object" + }, + "color": { + "description": "Line color.", + "type": "string" + }, + "dx": { + "description": "X offset nudge in pixels.", + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + "dy": { + "description": "Y offset nudge in pixels.", + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + "effect": { + "description": "Named animation preset for this element.", + "properties": { + "amplitude": { + "description": "Effect intensity: pixels of movement for float/scroll/drift, 0–1 scale for fade/pulse. Default varies by effect (float: 2px).", + "type": "number" + }, + "name": { + "description": "Named animation preset.", + "enum": [ + "float", + "scroll-left", + "scroll-right", + "pulse", + "blink", + "twinkle", + "drift", + "fade-in", + "fade-out" + ], + "type": "string" + }, + "period": { + "description": "Effect period in frames. Defaults to total scene frame count.", + "type": "number" + }, + "phase": { + "description": "Phase offset 0–1 to stagger this element relative to others (0.5 = half-cycle offset).", + "type": "number" + } + }, + "required": [ + "name" + ], + "type": "object" + }, + "opacity": { + "description": "Opacity 0–100 (default: 100).", + "maximum": 100, + "minimum": 0, + "type": "integer" + }, + "type": { + "const": "line", + "description": "Line element type.", + "type": "string" + }, + "visible": { + "description": "Whether the element is visible (default: true).", + "type": "boolean" + }, + "x0": { + "description": "Start X coordinate.", + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + "x1": { + "description": "End X coordinate.", + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + "y0": { + "description": "Start Y coordinate.", + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + "y1": { + "description": "End Y coordinate.", + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + } + }, + "required": [ + "type", + "x0", + "y0", + "x1", + "y1" + ], + "type": "object" + }, + { + "description": "Horizontal progress bar widget.", + "properties": { + "animate": { + "additionalProperties": { + "description": "Keyframe entries: [[frame, value], ...].", + "items": { + "prefixItems": [ + { + "description": "Frame index.", + "type": "number" + }, + { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string" + }, + { + "type": "boolean" + } + ], + "description": "Value at this frame." + } + ], + "type": "array" + }, + "type": "array" + }, + "description": "Raw keyframe animation map for this element.", + "propertyNames": { + "type": "string" + }, + "type": "object" + }, + "dx": { + "description": "X offset nudge in pixels.", + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + "dy": { + "description": "Y offset nudge in pixels.", + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + "effect": { + "description": "Named animation preset for this element.", + "properties": { + "amplitude": { + "description": "Effect intensity: pixels of movement for float/scroll/drift, 0–1 scale for fade/pulse. Default varies by effect (float: 2px).", + "type": "number" + }, + "name": { + "description": "Named animation preset.", + "enum": [ + "float", + "scroll-left", + "scroll-right", + "pulse", + "blink", + "twinkle", + "drift", + "fade-in", + "fade-out" + ], + "type": "string" + }, + "period": { + "description": "Effect period in frames. Defaults to total scene frame count.", + "type": "number" + }, + "phase": { + "description": "Phase offset 0–1 to stagger this element relative to others (0.5 = half-cycle offset).", + "type": "number" + } + }, + "required": [ + "name" + ], + "type": "object" + }, + "h": { + "description": "Height in pixels.", + "maximum": 9007199254740991, + "minimum": 1, + "type": "integer" + }, + "label": { + "description": "Optional label rendered on the bar.", + "type": "string" + }, + "max": { + "description": "Maximum value.", + "type": "number" + }, + "opacity": { + "description": "Opacity 0–100 (default: 100).", + "maximum": 100, + "minimum": 0, + "type": "integer" + }, + "palette": { + "description": "Named palette for gradient fill.", + "enum": [ + "ember", + "ice", + "neon", + "fire", + "lavender", + "claude", + "mono" + ], + "type": "string" + }, + "trackColor": { + "description": "Background track color.", + "type": "string" + }, + "type": { + "const": "progress", + "description": "Progress bar widget type.", + "type": "string" + }, + "value": { + "description": "Current value.", + "type": "number" + }, + "visible": { + "description": "Whether the element is visible (default: true).", + "type": "boolean" + }, + "w": { + "description": "Width in pixels.", + "maximum": 9007199254740991, + "minimum": 1, + "type": "integer" + }, + "x": { + "description": "X coordinate.", + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + "y": { + "description": "Y coordinate.", + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + } + }, + "required": [ + "type", + "x", + "y", + "w", + "h", + "value", + "max" + ], + "type": "object" + }, + { + "description": "Sparkline chart widget (line or bar).", + "properties": { + "animate": { + "additionalProperties": { + "description": "Keyframe entries: [[frame, value], ...].", + "items": { + "prefixItems": [ + { + "description": "Frame index.", + "type": "number" + }, + { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string" + }, + { + "type": "boolean" + } + ], + "description": "Value at this frame." + } + ], + "type": "array" + }, + "type": "array" + }, + "description": "Raw keyframe animation map for this element.", + "propertyNames": { + "type": "string" + }, + "type": "object" + }, + "color": { + "description": "Line or bar color.", + "type": "string" + }, + "data": { + "description": "Data points for the chart.", + "items": { + "type": "number" + }, + "minItems": 2, + "type": "array" + }, + "dx": { + "description": "X offset nudge in pixels.", + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + "dy": { + "description": "Y offset nudge in pixels.", + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + "effect": { + "description": "Named animation preset for this element.", + "properties": { + "amplitude": { + "description": "Effect intensity: pixels of movement for float/scroll/drift, 0–1 scale for fade/pulse. Default varies by effect (float: 2px).", + "type": "number" + }, + "name": { + "description": "Named animation preset.", + "enum": [ + "float", + "scroll-left", + "scroll-right", + "pulse", + "blink", + "twinkle", + "drift", + "fade-in", + "fade-out" + ], + "type": "string" + }, + "period": { + "description": "Effect period in frames. Defaults to total scene frame count.", + "type": "number" + }, + "phase": { + "description": "Phase offset 0–1 to stagger this element relative to others (0.5 = half-cycle offset).", + "type": "number" + } + }, + "required": [ + "name" + ], + "type": "object" + }, + "h": { + "description": "Height in pixels.", + "maximum": 9007199254740991, + "minimum": 2, + "type": "integer" + }, + "kind": { + "description": "Chart style (default: line).", + "enum": [ + "line", + "bar" + ], + "type": "string" + }, + "opacity": { + "description": "Opacity 0–100 (default: 100).", + "maximum": 100, + "minimum": 0, + "type": "integer" + }, + "type": { + "const": "sparkline", + "description": "Sparkline chart widget type.", + "type": "string" + }, + "visible": { + "description": "Whether the element is visible (default: true).", + "type": "boolean" + }, + "w": { + "description": "Width in pixels.", + "maximum": 9007199254740991, + "minimum": 2, + "type": "integer" + }, + "x": { + "description": "X coordinate.", + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + "y": { + "description": "Y coordinate.", + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + } + }, + "required": [ + "type", + "x", + "y", + "w", + "h", + "data" + ], + "type": "object" + }, + { + "description": "Bitmap element using explicit palette indices.", + "properties": { + "animate": { + "additionalProperties": { + "description": "Keyframe entries: [[frame, value], ...].", + "items": { + "prefixItems": [ + { + "description": "Frame index.", + "type": "number" + }, + { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string" + }, + { + "type": "boolean" + } + ], + "description": "Value at this frame." + } + ], + "type": "array" + }, + "type": "array" + }, + "description": "Raw keyframe animation map for this element.", + "propertyNames": { + "type": "string" + }, + "type": "object" + }, + "dx": { + "description": "X offset nudge in pixels.", + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + "dy": { + "description": "Y offset nudge in pixels.", + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + "effect": { + "description": "Named animation preset for this element.", + "properties": { + "amplitude": { + "description": "Effect intensity: pixels of movement for float/scroll/drift, 0–1 scale for fade/pulse. Default varies by effect (float: 2px).", + "type": "number" + }, + "name": { + "description": "Named animation preset.", + "enum": [ + "float", + "scroll-left", + "scroll-right", + "pulse", + "blink", + "twinkle", + "drift", + "fade-in", + "fade-out" + ], + "type": "string" + }, + "period": { + "description": "Effect period in frames. Defaults to total scene frame count.", + "type": "number" + }, + "phase": { + "description": "Phase offset 0–1 to stagger this element relative to others (0.5 = half-cycle offset).", + "type": "number" + } + }, + "required": [ + "name" + ], + "type": "object" + }, + "opacity": { + "description": "Opacity 0–100 (default: 100).", + "maximum": 100, + "minimum": 0, + "type": "integer" + }, + "palette": { + "description": "Color palette: array of hex colors indexed 0-F.", + "items": { + "type": "string" + }, + "type": "array" + }, + "rows": { + "description": "Row strings of hex palette indices (0-F, space = transparent).", + "items": { + "type": "string" + }, + "type": "array" + }, + "type": { + "const": "bitmap", + "description": "Bitmap element using palette indices.", + "type": "string" + }, + "visible": { + "description": "Whether the element is visible (default: true).", + "type": "boolean" + }, + "x": { + "description": "X coordinate.", + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + "y": { + "description": "Y coordinate.", + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + } + }, + "required": [ + "type", + "x", + "y", + "rows", + "palette" + ], + "type": "object" + }, + { + "description": "Sparse scatter of individually colored pixels.", + "properties": { + "animate": { + "additionalProperties": { + "description": "Keyframe entries: [[frame, value], ...].", + "items": { + "prefixItems": [ + { + "description": "Frame index.", + "type": "number" + }, + { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string" + }, + { + "type": "boolean" + } + ], + "description": "Value at this frame." + } + ], + "type": "array" + }, + "type": "array" + }, + "description": "Raw keyframe animation map for this element.", + "propertyNames": { + "type": "string" + }, + "type": "object" + }, + "data": { + "description": "Array of pixel positions and colors.", + "items": { + "description": "A single pixel: position and color.", + "properties": { + "color": { + "description": "Pixel color.", + "type": "string" + }, + "x": { + "description": "Pixel X coordinate.", + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + "y": { + "description": "Pixel Y coordinate.", + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + } + }, + "required": [ + "x", + "y", + "color" + ], + "type": "object" + }, + "type": "array" + }, + "dx": { + "description": "X offset nudge in pixels.", + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + "dy": { + "description": "Y offset nudge in pixels.", + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + "effect": { + "description": "Named animation preset for this element.", + "properties": { + "amplitude": { + "description": "Effect intensity: pixels of movement for float/scroll/drift, 0–1 scale for fade/pulse. Default varies by effect (float: 2px).", + "type": "number" + }, + "name": { + "description": "Named animation preset.", + "enum": [ + "float", + "scroll-left", + "scroll-right", + "pulse", + "blink", + "twinkle", + "drift", + "fade-in", + "fade-out" + ], + "type": "string" + }, + "period": { + "description": "Effect period in frames. Defaults to total scene frame count.", + "type": "number" + }, + "phase": { + "description": "Phase offset 0–1 to stagger this element relative to others (0.5 = half-cycle offset).", + "type": "number" + } + }, + "required": [ + "name" + ], + "type": "object" + }, + "opacity": { + "description": "Opacity 0–100 (default: 100).", + "maximum": 100, + "minimum": 0, + "type": "integer" + }, + "type": { + "const": "pixels", + "description": "Sparse pixel dots element.", + "type": "string" + }, + "visible": { + "description": "Whether the element is visible (default: true).", + "type": "boolean" + } + }, + "required": [ + "type", + "data" + ], + "type": "object" + }, + { + "description": "External image element (local path or https URL).", + "properties": { + "animate": { + "additionalProperties": { + "description": "Keyframe entries: [[frame, value], ...].", + "items": { + "prefixItems": [ + { + "description": "Frame index.", + "type": "number" + }, + { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string" + }, + { + "type": "boolean" + } + ], + "description": "Value at this frame." + } + ], + "type": "array" + }, + "type": "array" + }, + "description": "Raw keyframe animation map for this element.", + "propertyNames": { + "type": "string" + }, + "type": "object" + }, + "dx": { + "description": "X offset nudge in pixels.", + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + "dy": { + "description": "Y offset nudge in pixels.", + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + "effect": { + "description": "Named animation preset for this element.", + "properties": { + "amplitude": { + "description": "Effect intensity: pixels of movement for float/scroll/drift, 0–1 scale for fade/pulse. Default varies by effect (float: 2px).", + "type": "number" + }, + "name": { + "description": "Named animation preset.", + "enum": [ + "float", + "scroll-left", + "scroll-right", + "pulse", + "blink", + "twinkle", + "drift", + "fade-in", + "fade-out" + ], + "type": "string" + }, + "period": { + "description": "Effect period in frames. Defaults to total scene frame count.", + "type": "number" + }, + "phase": { + "description": "Phase offset 0–1 to stagger this element relative to others (0.5 = half-cycle offset).", + "type": "number" + } + }, + "required": [ + "name" + ], + "type": "object" + }, + "fit": { + "description": "Resize fit mode (default: contain).", + "enum": [ + "contain", + "cover", + "fill" + ], + "type": "string" + }, + "h": { + "description": "Target height (default: canvas height).", + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + "kernel": { + "description": "Resize kernel: nearest for pixel art, lanczos3 for photos (default: nearest).", + "enum": [ + "nearest", + "lanczos3", + "mitchell" + ], + "type": "string" + }, + "opacity": { + "description": "Opacity 0–100 (default: 100).", + "maximum": 100, + "minimum": 0, + "type": "integer" + }, + "source": { + "description": "Absolute local file path or https (not http) URL.", + "type": "string" + }, + "type": { + "const": "image", + "description": "Image element (local path or https URL).", + "type": "string" + }, + "visible": { + "description": "Whether the element is visible (default: true).", + "type": "boolean" + }, + "w": { + "description": "Target width (default: canvas width).", + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + "x": { + "description": "X offset on canvas (default: 0).", + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + "y": { + "description": "Y offset on canvas (default: 0).", + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + } + }, + "required": [ + "type", + "source" + ], + "type": "object" + }, + { + "description": "Sprite sheet element for character or tile art.", + "properties": { + "animate": { + "additionalProperties": { + "description": "Keyframe entries: [[frame, value], ...].", + "items": { + "prefixItems": [ + { + "description": "Frame index.", + "type": "number" + }, + { + "anyOf": [ + { + "type": "number" + }, + { + "type": "string" + }, + { + "type": "boolean" + } + ], + "description": "Value at this frame." + } + ], + "type": "array" + }, + "type": "array" + }, + "description": "Raw keyframe animation map for this element.", + "propertyNames": { + "type": "string" + }, + "type": "object" + }, + "bodyColor": { + "description": "Override body color.", + "type": "string" + }, + "cols": { + "description": "Number of columns in the sprite grid.", + "maximum": 9007199254740991, + "minimum": 1, + "type": "integer" + }, + "darkColor": { + "description": "Override dark/eye color.", + "type": "string" + }, + "dx": { + "description": "X offset nudge in pixels.", + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + "dy": { + "description": "Y offset nudge in pixels.", + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + "effect": { + "description": "Named animation preset for this element.", + "properties": { + "amplitude": { + "description": "Effect intensity: pixels of movement for float/scroll/drift, 0–1 scale for fade/pulse. Default varies by effect (float: 2px).", + "type": "number" + }, + "name": { + "description": "Named animation preset.", + "enum": [ + "float", + "scroll-left", + "scroll-right", + "pulse", + "blink", + "twinkle", + "drift", + "fade-in", + "fade-out" + ], + "type": "string" + }, + "period": { + "description": "Effect period in frames. Defaults to total scene frame count.", + "type": "number" + }, + "phase": { + "description": "Phase offset 0–1 to stagger this element relative to others (0.5 = half-cycle offset).", + "type": "number" + } + }, + "required": [ + "name" + ], + "type": "object" + }, + "opacity": { + "description": "Opacity 0–100 (default: 100).", + "maximum": 100, + "minimum": 0, + "type": "integer" + }, + "path": { + "description": "Absolute local path to the sprite sheet image.", + "type": "string" + }, + "rows": { + "description": "Number of rows in the sprite grid.", + "maximum": 9007199254740991, + "minimum": 1, + "type": "integer" + }, + "scale": { + "description": "Pixel scale factor.", + "maximum": 9007199254740991, + "minimum": 1, + "type": "integer" + }, + "type": { + "const": "sprite", + "description": "Sprite sheet element.", + "type": "string" + }, + "visible": { + "description": "Whether the element is visible (default: true).", + "type": "boolean" + }, + "x": { + "anyOf": [ + { + "description": "Absolute pixel X coordinate (0 = left edge).", + "type": "number" + }, + { + "description": "Semantic horizontal alignment.", + "enum": [ + "left", + "center", + "right" + ], + "type": "string" + } + ], + "description": "X position (default: center)." + }, + "y": { + "description": "Y position (default: 0).", + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + } + }, + "required": [ + "type", + "path", + "cols", + "rows" + ], + "type": "object" + } +] - changed
Input schema / properties / output / descriptionPrevious value: -"Explicit output file path for saving (overrides PIXOO_OUTPUT_DIR)."New value: +"Absolute, already-normalized path to save the first frame to, in addition to the PIXOO_OUTPUT_DIR auto-save when that is configured. Both paths are reported in outputFiles." - changed
Output schema / $schemaPrevious value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema" - added
Output schema / anyOfAdded value: +[ + { + "not": { + "required": [ + "error" + ] + }, + "required": [ + "pushed", + "frames", + "layout" + ] + }, + { + "required": [ + "error" + ] + } +] - added
Output schema / properties / errorAdded value: +{ + "additionalProperties": {}, + "description": "Present when the call failed. Absent on success.", + "properties": { + "code": { + "description": "JSON-RPC error code for this failure.", + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + "data": { + "additionalProperties": {}, + "properties": { + "reason": { + "description": "Machine-readable failure mode. Declared by this tool: `device_unreachable`: Device is not reachable over the network. `device_rejected`: Device firmware returned a non-zero error code. `no_device_configured`: PIXOO_IP is not set and push was requested. `asset_not_found`: An image or sprite path could not be read. `invalid_color`: A color value could not be resolved. `unknown_icon`: Icon name not found in the registry. `invalid_output_path`: The output path is relative or contains traversal segments. Other values are possible when a failure originates below the handler.", + "examples": [ + "device_unreachable", + "device_rejected", + "no_device_configured", + "asset_not_found", + "invalid_color", + "unknown_icon", + "invalid_output_path" + ], + "type": "string" + }, + "recovery": { + "additionalProperties": {}, + "description": "Actionable next step for the caller.", + "properties": { + "hint": { + "type": "string" + } + }, + "required": [ + "hint" + ], + "type": "object" + }, + "retryable": { + "description": "Whether retrying may succeed.", + "type": "boolean" + } + }, + "type": "object" + }, + "message": { + "description": "Human-readable description of what went wrong.", + "type": "string" + } + }, + "required": [ + "code", + "message" + ], + "type": "object" +} - removed
Output schema / properties / previewDataRemoved value: -{ - "description": "Base64-encoded PNG preview of the rendered scene (8× upscaled, 512px). For animations: the middle frame.", - "type": "string" -} - removed
Output schema / properties / previewMimeTypeRemoved value: -{ - "description": "MIME type of the preview image.", - "enum": [ - "image/png" - ], - "type": "string" -} - removed
Output schema / requiredRemoved value: -[ - "pushed", - "frames", - "layout" -]
- Changed
pixoo_control_device6 fields changed- changed
Input schema / $schemaPrevious value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema" - added
Input schema / additionalPropertiesAdded value: +false - changed
Output schema / $schemaPrevious value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema" - added
Output schema / anyOfAdded value: +[ + { + "not": { + "required": [ + "error" + ] + }, + "required": [ + "reachable", + "applied" + ] + }, + { + "required": [ + "error" + ] + } +] - added
Output schema / properties / errorAdded value: +{ + "additionalProperties": {}, + "description": "Present when the call failed. Absent on success.", + "properties": { + "code": { + "description": "JSON-RPC error code for this failure.", + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + "data": { + "additionalProperties": {}, + "properties": { + "reason": { + "description": "Machine-readable failure mode. Declared by this tool: `device_unreachable`: Device is not reachable. `device_rejected`: Device rejected the command. `no_device_configured`: PIXOO_IP is not set. Other values are possible when a failure originates below the handler.", + "examples": [ + "device_unreachable", + "device_rejected", + "no_device_configured" + ], + "type": "string" + }, + "recovery": { + "additionalProperties": {}, + "description": "Actionable next step for the caller.", + "properties": { + "hint": { + "type": "string" + } + }, + "required": [ + "hint" + ], + "type": "object" + }, + "retryable": { + "description": "Whether retrying may succeed.", + "type": "boolean" + } + }, + "type": "object" + }, + "message": { + "description": "Human-readable description of what went wrong.", + "type": "string" + } + }, + "required": [ + "code", + "message" + ], + "type": "object" +} - removed
Output schema / requiredRemoved value: -[ - "reachable", - "applied" -]
- Changed
pixoo_design_brief6 fields changed- changed
Input schema / $schemaPrevious value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema" - added
Input schema / additionalPropertiesAdded value: +false - changed
Output schema / $schemaPrevious value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema" - added
Output schema / anyOfAdded value: +[ + { + "not": { + "required": [ + "error" + ] + }, + "required": [ + "topic", + "craftGuidance", + "deviceContext", + "nextToolSuggestions", + "availableThemes", + "iconCategories" + ] + }, + { + "required": [ + "error" + ] + } +] - added
Output schema / properties / errorAdded value: +{ + "additionalProperties": {}, + "description": "Present when the call failed. Absent on success.", + "properties": { + "code": { + "description": "JSON-RPC error code for this failure.", + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + "data": { + "additionalProperties": {}, + "properties": { + "reason": { + "description": "Machine-readable failure mode.", + "type": "string" + }, + "recovery": { + "additionalProperties": {}, + "description": "Actionable next step for the caller.", + "properties": { + "hint": { + "type": "string" + } + }, + "required": [ + "hint" + ], + "type": "object" + }, + "retryable": { + "description": "Whether retrying may succeed.", + "type": "boolean" + } + }, + "type": "object" + }, + "message": { + "description": "Human-readable description of what went wrong.", + "type": "string" + } + }, + "required": [ + "code", + "message" + ], + "type": "object" +} - removed
Output schema / requiredRemoved value: -[ - "topic", - "craftGuidance", - "deviceContext", - "nextToolSuggestions", - "availableThemes", - "iconCategories" -]
- Changed
pixoo_discover_devices6 fields changed- changed
Input schema / $schemaPrevious value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema" - added
Input schema / additionalPropertiesAdded value: +false - changed
Output schema / $schemaPrevious value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema" - added
Output schema / anyOfAdded value: +[ + { + "not": { + "required": [ + "error" + ] + }, + "required": [ + "devices" + ] + }, + { + "required": [ + "error" + ] + } +] - added
Output schema / properties / errorAdded value: +{ + "additionalProperties": {}, + "description": "Present when the call failed. Absent on success.", + "properties": { + "code": { + "description": "JSON-RPC error code for this failure.", + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + "data": { + "additionalProperties": {}, + "properties": { + "reason": { + "description": "Machine-readable failure mode. Declared by this tool: `discovery_failed`: Divoom cloud discovery endpoint is unreachable. Other values are possible when a failure originates below the handler.", + "examples": [ + "discovery_failed" + ], + "type": "string" + }, + "recovery": { + "additionalProperties": {}, + "description": "Actionable next step for the caller.", + "properties": { + "hint": { + "type": "string" + } + }, + "required": [ + "hint" + ], + "type": "object" + }, + "retryable": { + "description": "Whether retrying may succeed.", + "type": "boolean" + } + }, + "type": "object" + }, + "message": { + "description": "Human-readable description of what went wrong.", + "type": "string" + } + }, + "required": [ + "code", + "message" + ], + "type": "object" +} - removed
Output schema / requiredRemoved value: -[ - "devices" -]
- Changed
pixoo_display_text8 fields changed- changed
Input schema / $schemaPrevious value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema" - added
Input schema / additionalPropertiesAdded value: +false - changed
Output schema / $schemaPrevious value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema" - added
Output schema / anyOfAdded value: +[ + { + "not": { + "required": [ + "error" + ] + }, + "required": [ + "pushed", + "layout" + ] + }, + { + "required": [ + "error" + ] + } +] - added
Output schema / properties / errorAdded value: +{ + "additionalProperties": {}, + "description": "Present when the call failed. Absent on success.", + "properties": { + "code": { + "description": "JSON-RPC error code for this failure.", + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + "data": { + "additionalProperties": {}, + "properties": { + "reason": { + "description": "Machine-readable failure mode. Declared by this tool: `device_unreachable`: Device is not reachable over the network. `device_rejected`: Device firmware returned a non-zero error code. `no_device_configured`: PIXOO_IP is not set. `invalid_color`: A color value could not be resolved. Other values are possible when a failure originates below the handler.", + "examples": [ + "device_unreachable", + "device_rejected", + "no_device_configured", + "invalid_color" + ], + "type": "string" + }, + "recovery": { + "additionalProperties": {}, + "description": "Actionable next step for the caller.", + "properties": { + "hint": { + "type": "string" + } + }, + "required": [ + "hint" + ], + "type": "object" + }, + "retryable": { + "description": "Whether retrying may succeed.", + "type": "boolean" + } + }, + "type": "object" + }, + "message": { + "description": "Human-readable description of what went wrong.", + "type": "string" + } + }, + "required": [ + "code", + "message" + ], + "type": "object" +} - removed
Output schema / properties / previewDataRemoved value: -{ - "description": "Base64-encoded PNG preview of the rendered frame (8× upscaled, 512px).", - "type": "string" -} - removed
Output schema / properties / previewMimeTypeRemoved value: -{ - "description": "MIME type of the preview image.", - "enum": [ - "image/png" - ], - "type": "string" -} - removed
Output schema / requiredRemoved value: -[ - "pushed", - "layout" -]
- Changed
pixoo_overlay_text6 fields changed- changed
Input schema / $schemaPrevious value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema" - added
Input schema / additionalPropertiesAdded value: +false - changed
Output schema / $schemaPrevious value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema" - added
Output schema / anyOfAdded value: +[ + { + "not": { + "required": [ + "error" + ] + }, + "required": [ + "acknowledged", + "mode", + "id" + ] + }, + { + "required": [ + "error" + ] + } +] - added
Output schema / properties / errorAdded value: +{ + "additionalProperties": {}, + "description": "Present when the call failed. Absent on success.", + "properties": { + "code": { + "description": "JSON-RPC error code for this failure.", + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + "data": { + "additionalProperties": {}, + "properties": { + "reason": { + "description": "Machine-readable failure mode. Declared by this tool: `device_unreachable`: Device is not reachable. `device_rejected`: Device firmware rejected the command. `no_device_configured`: PIXOO_IP is not set. `invalid_color`: The color value could not be resolved. Other values are possible when a failure originates below the handler.", + "examples": [ + "device_unreachable", + "device_rejected", + "no_device_configured", + "invalid_color" + ], + "type": "string" + }, + "recovery": { + "additionalProperties": {}, + "description": "Actionable next step for the caller.", + "properties": { + "hint": { + "type": "string" + } + }, + "required": [ + "hint" + ], + "type": "object" + }, + "retryable": { + "description": "Whether retrying may succeed.", + "type": "boolean" + } + }, + "type": "object" + }, + "message": { + "description": "Human-readable description of what went wrong.", + "type": "string" + } + }, + "required": [ + "code", + "message" + ], + "type": "object" +} - removed
Output schema / requiredRemoved value: -[ - "acknowledged", - "mode", - "id" -]
- Changed
pixoo_push_image8 fields changed- changed
Input schema / $schemaPrevious value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema" - added
Input schema / additionalPropertiesAdded value: +false - changed
Output schema / $schemaPrevious value: -"http://json-schema.org/draft-07/schema#"New value: +"https://json-schema.org/draft/2020-12/schema" - added
Output schema / anyOfAdded value: +[ + { + "not": { + "required": [ + "error" + ] + }, + "required": [ + "pushed" + ] + }, + { + "required": [ + "error" + ] + } +] - added
Output schema / properties / errorAdded value: +{ + "additionalProperties": {}, + "description": "Present when the call failed. Absent on success.", + "properties": { + "code": { + "description": "JSON-RPC error code for this failure.", + "maximum": 9007199254740991, + "minimum": -9007199254740991, + "type": "integer" + }, + "data": { + "additionalProperties": {}, + "properties": { + "reason": { + "description": "Machine-readable failure mode. Declared by this tool: `device_unreachable`: Device is not reachable. `device_rejected`: Device firmware returned a non-zero error code. `no_device_configured`: PIXOO_IP is not set. `asset_not_found`: Image path or URL could not be read. Other values are possible when a failure originates below the handler.", + "examples": [ + "device_unreachable", + "device_rejected", + "no_device_configured", + "asset_not_found" + ], + "type": "string" + }, + "recovery": { + "additionalProperties": {}, + "description": "Actionable next step for the caller.", + "properties": { + "hint": { + "type": "string" + } + }, + "required": [ + "hint" + ], + "type": "object" + }, + "retryable": { + "description": "Whether retrying may succeed.", + "type": "boolean" + } + }, + "type": "object" + }, + "message": { + "description": "Human-readable description of what went wrong.", + "type": "string" + } + }, + "required": [ + "code", + "message" + ], + "type": "object" +} - removed
Output schema / properties / previewDataRemoved value: -{ - "description": "Base64-encoded PNG preview of the downsampled 64×64 result (8× upscaled, 512px).", - "type": "string" -} - removed
Output schema / properties / previewMimeTypeRemoved value: -{ - "description": "MIME type of the preview image.", - "enum": [ - "image/png" - ], - "type": "string" -} - removed
Output schema / requiredRemoved value: -[ - "pushed" -]
7 tool updates
v1.0.0- First observed
pixoo_compose_scene - First observed
pixoo_control_device - First observed
pixoo_design_brief - First observed
pixoo_discover_devices - First observed
pixoo_display_text - First observed
pixoo_overlay_text - First observed
pixoo_push_image
TDQS
Each tool has a clearly distinct purpose: text rendering, scene composition, image pushing, device control, persistent overlays, discovery, and design guidance. Cross-references explicitly explain when to use display_text versus compose_scene versus overlay_text, eliminating ambiguity.
All tools follow the same pixoo_ prefix and a verb_noun pattern, such as display_text, compose_scene, push_image, control_device, and discover_devices. Naming is uniform, predictable, and readable with no mixed conventions.
Seven tools is a well-scoped size for a Pixoo LED display server, covering setup, content authoring, display operations, and device management without unnecessary duplication. Each tool earns its place and contributes a meaningful capability.
The server covers the main workflows well: discovery, device state, text, images, scenes, overlays, and design guidance. Minor missing operations like a simple clear/reset display method would make coverage more complete, but most users can work around it or use screen-off/push-blank behavior.
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Connectors
Manage digital signage screens, playlists and media from your AI assistant.
Display delivery platform for AI agents. Push HTML, dashboards and live data to screens.
Control Android TV from any AI. 38 MCP tools: playback, recap, recommend, smart-home, schedules.
Build and run visual creative-production workflows from your AI agent.
Related MCP Servers
- AlicenseBqualityBmaintenanceEnables AI assistants to create and manipulate pixel art by providing tools for drawing, layer management, and animation frame control. It supports exporting projects to PNG, GIF, and sprite sheet formats via the Model Context Protocol.2214Apache 2.0

mcp-divoom-lanofficial
AlicenseBqualityCmaintenanceEnables AI clients to control Divoom LAN devices, including watchface management, brightness adjustment, and screen control through natural language.18632MIT- AlicenseBqualityCmaintenanceEnables LLMs to create and edit pixel art reliably with support for layers, frames, symmetry, and various drawing tools.70MIT No Attribution
- FlicenseAqualityDmaintenanceEnables Claude to create, update, and manage LED patterns on a PixelBlaze controller, including setting brightness and reading device info, through a set of tools that interact with the device's API.14-
Appeared in Searches
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/cyanheads/pixoo-mcp-server'
If you have feedback or need assistance with the MCP directory API, please join our Discord server