WallCraft SDK Preview • Compatible with Windows 10 and 11

Developer Documentation

Official developer reference for Wallcraft. Build isolated, high-performance desktop widgets using standard HTML5/CSS3/JavaScript, and create interactive 3D live wallpapers using WebGL 2.0 GLSL shaders.

Architecture & Sandbox Security

Wallcraft executes all community widgets inside strict HTML5 <iframe sandbox="allow-scripts"> containers with an opaque (null) origin. External widgets have zero direct access to Node.js, Electron preload bridges, or local file systems.

All interaction between your widget and the desktop environment takes place via an asynchronous, strictly validated postMessage RPC channel exposed through the window.WallCraft SDK.

Architecture Diagram Wallcraft Runtime Isolation
┌─────────────────────────────────────────────────────────────────────────┐
│                           WALLCRAFT RUNTIME                             │
├─────────────────────────────────────────────────────────────────────────┤
│  Community Widgets (user-widgets)                                       │
│  - Isolated HTML5 Iframe Sandbox (sandbox="allow-scripts")               │
│  - Opaque Origin (null) — No direct OS/Node.js access; permission-gated host bridge                │
│  - window.WallCraft SDK (PostMessage RPC Bridge)                        │
│  - Self-contained package (.zip with manifest.json, html, css, js)       │
└─────────────────────────────────────────────────────────────────────────┘
Zero Native Dependencies
You do not need to install C++, Rust, or Python tools to create widgets. Standard web development tools (VS Code, Chrome DevTools, any text editor) are all you need.
Public SDK & Compatibility Alias (window.WallCraft & window.wallcraft)
Use window.WallCraft in community widgets. The lowercase sandbox alias exposes only a subset and names the audio object audioMixer; it does not include config, language or lifecycle hooks. In the host window, window.wallcraft is a different, privileged preload API.

Community Sandbox vs. Privileged Preload Layer

Wallcraft maintains a strict security perimeter between third-party community widgets and the native operating system. Community widgets execute inside an isolated <iframe sandbox="allow-scripts"> and interact exclusively through the injected window.WallCraft SDK. Direct Electron host preload APIs (also named window.wallcraft in the host context) are restricted to privileged internal components and settings panels.

Feature / Subsystem Sandbox SDK (window.WallCraft) Host Preload Bridge (window.wallcraft) Execution Context & Permission
WASAPI Audio Mixer window.WallCraft.audio.* window.wallcraft.audioMixer.* PostMessage RPC bridge (Requires "audio" permission)
Declarative Modals window.WallCraft.showModal(options) WallCraftWallpaperModals.showModal() PostMessage RPC bridge (Native acrylic UI dialog)
Dynamic Configuration window.WallCraft.config, onUpdate(cb) Electron Store IPC Delivered on init & merged on user settings change
Language (i18n) window.WallCraft.language, onLanguageChange window.wallcraft.changeLanguage Active locale delivered via postMessage ("en", "tr")
Lifecycle Hooks onPause, onResume, onDestroy Window lifecycle events Sent during gaming mode, occlusion, or deletion
System Telemetry (CPU/RAM/GPU) window.WallCraft.getSystemStats() window.wallcraft.getSystemStats() PostMessage RPC bridge (Requires "system-stats" permission)
Media Controls (SMTC) window.WallCraft.onMediaInfo, mediaControl window.wallcraft.onMediaInfo, mediaControl PostMessage RPC bridge (Requires "media" permission)
Discord Voice RPC window.WallCraft.getDiscordVoiceState(), etc. window.wallcraft.getDiscordVoiceState(), etc. PostMessage RPC bridge (Requires "discord" permission)
App Launcher (Shortcuts) window.WallCraft.launchShortcut() window.wallcraft.launchShortcut() PostMessage RPC bridge (Requires "launcher" permission)
App & File Icons window.WallCraft.getFileIcon() window.wallcraft.getFileIcon() PostMessage RPC bridge (Automatic shell icon extraction)
Native File Pickers selectImage(), selectVideo(), etc. selectImage(), selectVideo(), etc. PostMessage RPC bridge (Win32 Common Dialogs)
Clipboard Access Synthetic shortcuts (Ctrl+C, Ctrl+V) window.wallcraft.writeClipboardText(text) Synthetic keyboard bridge vs Programmatic host write
Diagnostics & Feedback Unavailable in sandbox window.wallcraft.getSystemDiagnostics() Host Settings UI Only (Troubleshooting)
Widget Management Unavailable in sandbox window.wallcraft.getAvailableWidgets(), etc. Host Settings UI Only (Package Manager)

Compatibility: SDK Preview, checked against application working sources labelled 0.0.1-beta on 13 October 2026. This is a source compatibility reference, not confirmation of a published binary. Host-only methods are grouped in Advanced Host Reference; they are unavailable to community widgets.

InterfaceScope
window.WallCraftPublic sandbox SDK: config, language, lifecycle hooks, audio and permission-gated services.
window.wallcraft (sandbox)Compatibility subset; audio is audioMixer. Prefer the public SDK.
window.wallcraft (host)Privileged application internals; not a community SDK.

Quick Start: Build Your First Widget in 5 Minutes

Every Wallcraft widget is simply a local folder containing 4 core code files, plus optional dedicated media assets:

Directory Structure my-clock-widget/
my-clock-widget/
├── manifest.json   # Metadata, grid span (cols/rows), and config schema
├── widget.html     # Markup & layout elements
├── widget.css      # Theme styling with CSS design tokens
├── widget.js       # Lifecycle logic (init, update, destroy)
├── icon.png        # (Optional) Dedicated tile icon in Settings (icon.png/webp/svg)
└── preview.webp    # (Optional) Fullscreen catalog/hub preview (preview.webp/png)

Here is a complete, minimal "Live Clock" widget:

JSON manifest.json
{
  "id": "minimal-clock",
  "name": "Minimal Digital Clock",
  "version": "1.0.0",
  "author": "Your Name",
  "description": "A sleek, theme-adaptive desktop clock.",
  "entry": {
    "html": "widget.html",
    "script": "widget.js",
    "css": "widget.css"
  },
  "gridSpan": {
    "cols": 4,
    "rows": 2
  },
  "wrapper": "default",
  "icon": "icon.png",
  "previewImage": "preview.webp",
  "configSchema": [
    {
      "key": "showSeconds",
      "label": "Show Seconds",
      "type": "boolean",
      "default": true
    }
  ]
}
HTML widget.html
<div class="clock-container">
  <div class="clock-header">
    <span class="clock-label">SYSTEM CLOCK</span>
    <span class="clock-badge">LIVE</span>
  </div>
  <div id="clock-display" class="clock-digits">--:--:--</div>
</div>
CSS widget.css
/* Container adapted to wrapper theme tokens */
.clock-container {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  width: 100%;
  height: 100%;
  box-sizing: border-box;
  padding: 1.25rem;
  font-family: var(--wc-font-primary, 'Inter', sans-serif);
}

.clock-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  width: 100%;
  margin-bottom: 0.5rem;
}

.clock-label {
  font-size: 0.75rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  color: var(--wc-theme-text-sub, #94a3b8);
}

.clock-badge {
  font-size: 0.65rem;
  font-weight: 700;
  padding: 2px 6px;
  border-radius: var(--wc-theme-radius-btn, 6px);
  background: var(--wc-theme-primary, #FF5A36);
  color: #000000;
}

.clock-digits {
  font-size: 2.25rem;
  font-weight: 700;
  font-family: var(--wc-font-mono, 'JetBrains Mono', monospace);
  color: var(--wc-theme-text-main, #ffffff);
  letter-spacing: -0.02em;
}
JavaScript widget.js
(function () {
  'use strict';

  window.WallCraftWidget = {
    container: null,
    timerId: null,
    showSeconds: true,

    init: function (container, config) {
      this.container = container;
      this.update(config);
      this.tick();
      this.timerId = setInterval(() => this.tick(), 1000);
    },

    update: function (config) {
      if (!config) return;
      if (typeof config.showSeconds === 'boolean') {
        this.showSeconds = config.showSeconds;
        this.tick();
      }
    },

    tick: function () {
      if (!this.container) return;
      const clockEl = this.container.querySelector('#clock-display');
      if (!clockEl) return;

      const d = new Date();
      const h = String(d.getHours()).padStart(2, '0');
      const m = String(d.getMinutes()).padStart(2, '0');
      const s = String(d.getSeconds()).padStart(2, '0');

      clockEl.textContent = this.showSeconds ? `${h}:${m}:${s}` : `${h}:${m}`;
    },

    destroy: function () {
      if (this.timerId) {
        clearInterval(this.timerId);
        this.timerId = null;
      }
      this.container = null;
    }
  };
})();

Live Interactive Widget Preview

LIVE RUNTIME DEMO
SYSTEM CLOCK LIVE
12:34:56

Download the clock example. Extract it and open preview.html for a browser-only layout/lifecycle preview. Import the extracted folder or ZIP into WallCraft to test the actual injected SDK. The snippets above illustrate the files; widget.html alone is markup and does not load CSS, JavaScript or call init.

Testing & Debugging Workflow

Because Wallcraft runs as a native background desktop canvas behind Windows desktop icons (WorkerW / Progman layer), standard global browser shortcuts like F12 are not intercepted by the desktop wallpaper window. Use the following proven workflow to develop and debug:

  1. 1. Local Layout Preview: Open the downloaded clock’s preview.html in a browser. For your own widget, provide a preview harness that loads CSS/JS and calls init; it cannot emulate native SDK services. Press F12 to open DevTools, inspect DOM/CSS layout, and debug your JavaScript logic with breakpoints and live network tabs.
  2. 2. In-App Terminal Logging: When testing your imported widget inside Wallcraft, launch the application from the command line (npm start or Developer build). All console.log, console.warn, and console.error messages inside the sandboxed iframe are forwarded via wc:sandbox-log and output to the host terminal prefixed with [Widget:<widget-id>].
  3. 3. Live Reload via Settings: Import your widget folder via Settings → Modules & Widgets → "Import Folder". After editing your local files, toggle the widget off and back on in Settings to instantly reload the iframe without restarting Wallcraft.

PostMessage RPC Protocol & Error Guarantees

Sandboxed community widgets communicate with the WallCraft host through asynchronous JSON messages over the HTML5 postMessage bridge.

Message Type Direction Description
wc:init Host → Widget Delivers initial config and language. Triggers widget init().
wc:update Host → Widget Delivers complete merged config object on user settings modification.
wc:pause Host → Widget Sent when games run or desktop is occluded. Widget must pause rAF loops and intervals.
wc:resume Host → Widget Sent when desktop is revealed. Widget restarts timers and animations.
wc:language-change Host → Widget Broadcasts updated language code ("en", "tr").
wc:theme-update Host → Widget Delivers updated CSS theme variables ({ variables }) for live instant styling.
wc:audio-state-update Host → Widget Broadcasts live WASAPI master volume, peak levels, and process sessions.
wc:audio-request Widget → Host Sends WASAPI volume or mute change requests ({ reqId, action, ...args }).
wc:audio-response Host → Widget Resolves or rejects pending audio SDK promises ({ reqId, result, error }).
wc:system-stats-request Widget → Host Requests real-time CPU, RAM, GPU, and uptime telemetry.
wc:system-stats-response Host → Widget Returns system telemetry payload ({ reqId, result, error }).
wc:request-media-info Widget → Host Requests immediate refresh of Windows SMTC Now Playing metadata.
wc:media-info Host → Widget Broadcasts updated Now Playing track title, artist, album, thumbnail, and timeline.
wc:media-control Widget → Host Sends playback command ('play-pause', 'next', 'previous', etc.).
wc:discord-request Widget → Host Sends Discord Voice commands ('getState', 'toggleMute', 'toggleDeaf', etc.).
wc:discord-response Host → Widget Returns Discord RPC voice state response ({ reqId, result, error }).
wc:discord-voice-state-update Host → Widget Broadcasts Discord voice room connection changes and member rosters.
wc:discord-voice-speaking-update Host → Widget Broadcasts user speaking indicator state ({ userId, speaking, isSpeaking }).
wc:file-request Widget → Host Requests file picker dialogs, shell shortcut launch, or icon extraction.
wc:file-response Host → Widget Returns chosen file paths or execution status ({ reqId, result, error }).
wc:request-modal Widget → Host Opens native acrylic declarative modal dialog ({ reqId, widgetId, options }).
wc:modal-result Host → Widget Delivers user modal confirmation values or cancellation.
wc:synthetic-pointer Host → Widget Dispatches native desktop mouse clicks into the sandboxed iframe.
wc:synthetic-keyboard Host → Widget Forwards keystrokes from global Win32 hook when an input element is focused.
wc:blur-active-input Host → Widget Blurs active input when user clicks away on the desktop wallpaper.
wc:destroy Host → Widget Final signal before iframe unmounting. Triggers widget destroy().
Asynchronous Lifecycle & Clean Destruction Guarantees
Host RPC requests await completion asynchronously without an artificial timer. If a widget iframe is unmounted or destroyed while an asynchronous operation is in flight, the runtime immediately broadcasts wc:destroy: pending audio promises reject with Error('Widget destroyed'), while pending modal promises cleanly resolve to null to prevent unhandled rejection crashes.

Standardized RPC Error Catalog

Error Message Trigger Condition Recommended Widget Handling
"Widget destroyed" Widget iframe was unmounted while an asynchronous audio or modal request was in flight. Expected teardown behavior; ignore in widget catch blocks or cancel pending operations in destroy().
"Audio Mixer service unavailable" Host Windows WASAPI Core Audio daemon is offline or IPC connection disconnected. Show fallback volume controls or display an offline audio state in UI.
"Unauthorized audio request origin" Request sender does not match a verified, active sandboxed community widget instance. Security check failed; ensure widget runs inside official Wallcraft runtime environment.
"Security violation: spoofed widget identity" Sender iframe attempted to request a modal using a mismatched or forged widgetId. Ensure your options payload matches the id specified in manifest.json.
"Options object is required for WallCraft.showModal" WallCraft.showModal() was invoked with undefined, null, or a non-object. Pass a valid options configuration object containing at least title.
"Modal service not available" Wallpaper modal overlay renderer is not initialized or is restarting. Retry with exponential backoff or inform user to reopen the modal.
"Volume must be a valid number" setMasterVolume or setAppVolume received NaN or a non-numeric argument. Clamp volume values to an integer between 0 and 100.
"Invalid PID" setAppVolume or setAppMute was provided with an invalid process ID. Verify PID matches a positive integer returned by WallCraft.audio.getState().
"Unsupported audio action: [action]" An unrecognized action payload was posted across the audio RPC channel. Use only supported methods: getState, setMasterVolume, setMasterMute, setAppVolume, setAppMute.

This is a private JSON-message postMessage protocol, not JSON-RPC 2.0. Prefer SDK methods to sending raw messages. Promise rejection reports bridge/permission errors; a resolved result may still represent cancellation or failure. There is no automatic timeout. Pending modal requests resolve to null on destroy; pending audio/generic requests reject. Fire-and-forget media commands do not report completion.

Bridge envelope
// Illustrative SDK envelope; do not bypass permission checks.
const request = {type: 'wc:audio-request', reqId: 'unique-id', action: 'getState'};
// Response: {type: 'wc:audio-response', reqId: 'unique-id', result: state}
// Error:    {type: 'wc:audio-response', reqId: 'unique-id', error: '...'}
Method / parametersReturn / cancellationPermission / cleanup
onUpdate(cb), onLanguageChange(cb), onPause(cb), onResume(cb), onDestroy(cb)void; callbacks are attached to the widget instance lifetime.No capability permission; do not register repeatedly during update.
config, languageInitial configuration and active language; updates arrive through callbacks.No permission.
showModal(options)Promise<object|null>; cancel/destroy → null; invalid options reject.No capability permission; handle null before using fields.

manifest.json Package Reference

The manifest.json file defines your widget identity, layout requirements, dynamic settings form, and theme wrapper.

Field Type Status Description
id string Required 1–128 characters: letters, digits, underscore, hyphen and dot; the first character cannot be a dot. No .., trailing dot, reserved Windows names (CON/PRN/AUX/NUL/COM1–9/LPT1–9, including extensions), or __proto__, constructor, prototype (case-insensitive). Valid: sys-monitor; invalid: .hidden, x..y, CON.
name string Recommended Display title shown in the Wallcraft widget catalog and settings. Different items may share this name; identity is determined by ID.
instanceName string Optional Exact window property containing the lifecycle object, e.g. "MyCustomWidget" for window.MyCustomWidget. Allowed characters: letters, digits, underscore, dollar sign, dot and hyphen. Read as a literal property, not a nested path. If omitted, the name is derived from the last dot-separated ID segment, with hyphenated words capitalized and Widget appended (e.g. my-custom → MyCustomWidget). The runtime also accepts window.WallCraftWidget. Creator Studio preserves the original instance name when assigning a publication ID; retain this field in exported packages.
version string Recommended SemVer format (e.g., "1.0.0").
entry object Recommended Object containing relative paths: { "html": "...", "script": "...", "css": "..." }.
gridSpan object Recommended Slots occupied on 24×24 grid: { "cols": 1..24, "rows": 1..24 }.
wrapper string Optional Frame style: "default" follows the active desktop theme; "squircle-acrylic" explicitly selects WallCraft Default. Other keys include "flat-dark", "flat-light" (Liquid Glass), "cyber-obsidian", and "none" for frameless mode. See the complete list under Design Tokens. Defaults to "default".
pinMode string Optional "default" (desktop layer), "top-clickable" (always-on-top interactive), "top-non-clickable" (HUD ghost).
icon string Optional Dedicated widget tile icon path (e.g. "icon.png", "icon.webp", "icon.svg") or URL. Automatically detected from root directory candidate files (icon.png, icon.webp, icon.svg, icon.jpg, icon.jpeg) if omitted.
previewImage string Optional Fullscreen preview image path (e.g. "preview.webp", "preview.png") or URL. Automatically detected from root directory candidate files (preview.webp, preview.png, preview.jpg, preview.jpeg, preview.svg) if omitted.
permissions string[] Optional Declared capability scopes: ["system-stats", "audio", "media", "discord", "launcher", "network"].
configSchema array Optional Array of config definitions rendered as an interactive modal in Wallcraft.

Widget Media Detection: Dedicated Icons & Fullscreen Previews

Wallcraft features an intelligent dual-media detection engine (detectWidgetMedia in widget-manager.js) for both built-in and community extensions. Developers can supply a dedicated tile icon for the Modules & Widgets catalog as well as high-definition preview images for the marketplace, completely independently:

  • Dedicated Tile Icon (manifest.icon / icon.png): Rendered on the widget card/tile inside Wallcraft Settings. The runtime checks manifest.icon or manifest.iconImage first; if omitted, it automatically scans the widget folder for icon.png, icon.webp, icon.svg, icon.jpg, or icon.jpeg. If no icon file is found, Wallcraft gracefully falls back to the preview image, or displays an elegant two-letter uppercase monogram over an algorithmic 135-degree procedural gradient.
  • Fullscreen Preview (manifest.previewImage / preview.webp): Used for full-resolution showcase previews, marketplace detail modals, and catalog cards. The runtime checks manifest.previewImage or manifest.previewUrl; if omitted, it automatically scans the widget folder for preview.webp, preview.png, preview.jpg, preview.jpeg, or preview.svg.
  • Zero-Config Auto-Discovery: Declaring icon or previewImage in manifest.json is entirely optional. Simply dropping icon.png and preview.webp into your widget root folder enables instant auto-detection upon folder or .zip import.

Declared Permissions Reference

Permission Scope Target API Bridge Description
"system-stats" window.WallCraft.getSystemStats() Grants real-time hardware telemetry: CPU load/temp, RAM used/total, GPU load/temp/VRAM, and Disk I/O.
"audio" window.WallCraft.audio Grants Windows WASAPI Core Audio volume adjustments, peak metering, and per-process audio sessions.
"media" window.WallCraft.onMediaInfo / mediaControl Grants Windows SMTC transport controls (Spotify, Chrome, Apple Music metadata and playback).
"discord" window.WallCraft.getDiscordVoiceState Grants Discord Voice RPC channel state, speaking indicators, and mute/deafen actions.
"launcher" window.WallCraft.launchShortcut(path) Grants executable launching capability for desktop shortcuts and .exe application binaries.
"network" fetch() (HTTPS) Approved HTTPS connections only. CSP currently uses connect-src https:; WebSocket is not a supported contract. Servers must permit the sandbox’s opaque origin through CORS.

The importer enforces a safe id. Supply name, version, entry and gridSpan for a predictable package; they are recommendations rather than a complete JSON Schema validation. Missing version falls back to 1.0.0 in package metadata. Entry files are checked/read later by the import and loader paths; a manifest accepted by the ID check alone is not necessarily a runnable widget.

Declaring a permission does not grant it. WallCraft asks for user approval and stores the decision against the package fingerprint. Denial leaves the capability unavailable; changed package contents require a new decision. Handle denied calls and explain why each scope is needed. The launcher also checks the selected target and may ask for an additional security confirmation.

HTTPS helper
async function loadJson(url, signal) {
  const response = await fetch(url, {signal});
  if (!response.ok) throw new Error(`HTTP ${response.status}`);
  return response.json();
}
// Declare "network" and obtain user approval first.
// Call with your HTTPS endpoint that permits opaque-origin CORS.
// Abort via AbortController on pause/destroy; catch network/permission errors.

24×24 Magnetic Grid & Viewport Scaling

Wallcraft divides the user desktop into a 24 horizontal columns × 24 vertical rows grid. When widgets are dragged, they magnetically snap to grid intersections and center axes.

Grid Slots (cols×rows) 1080p (1920×1080) 1440p (2560×1440) 4K UHD (3840×2160) Recommended Use Case
2 × 2 (Compact Mini) 160px × 90px 213px × 120px 320px × 180px Mini icon, battery badge, counter
4 × 2 (Standard Badge) 320px × 90px 426px × 120px 640px × 180px Digital clock, mini hardware meter
6 × 3 (Horizontal Card) 480px × 135px 640px × 180px 960px × 270px Weather report, compact music player
6 × 4 (Panel Card) 480px × 180px 640px × 240px 960px × 360px Full audio mixer, notes list, calendar
8 × 5 (Dashboard Module) 640px × 225px 853px × 300px 1280px × 450px Multi-band audio visualizer, GPU monitor

Display Scaling (Ultrawide & Portrait)

Aspect Ratio Resolution 1 Grid Slot (Width × Height) 6 × 4 Panel Card Dimensions
Standard 16:9 1920 × 1080 (FHD) 80px × 45px 480px × 180px
Standard 16:9 2560 × 1440 (2K QHD) 106.6px × 60px 640px × 240px
Standard 16:9 3840 × 2160 (4K UHD) 160px × 90px 960px × 360px
Ultrawide 21:9 3440 × 1440 (UWQHD) 143.3px × 60px 860px × 240px
Super Ultrawide 32:9 5120 × 1440 (DQHD) 213.3px × 60px 1280px × 240px
Portrait 9:16 1080 × 1920 (Vertical) 45px × 80px 270px × 320px
Fluid Layout Advice
Always style widget contents using CSS flexbox or grid with relative percentages (width: 100%; height: 100%) so panels scale proportionally on both ultrawide gaming monitors and vertical programmer displays.

JavaScript Lifecycle (`init`, `update`, `destroy`)

Every widget script exports an object containing the core lifecycle hooks. Wallcraft triggers these methods during initialization, settings change, and application shutdown:

JavaScript widget.js Lifecycle Interface
window.WallCraftWidget = {
  container: null,

  // 1. Called on initial mount
  init: function (container, config) {
    this.container = container;
    this.update(config);
  },

  // 2. Called whenever user edits settings
  update: function (config) {
    // Update DOM styles or intervals
  },

  // 3. Called when desktop is obscured / gaming mode
  pause: function () {
    // Suspend custom animations
  },

  // 4. Called when desktop returns to view
  resume: function () {
    // Resume animations
  },

  // 5. Called before widget is deleted
  destroy: function () {
    this.container = null;
  }
};

See Resource Cleanup for a restartable requestAnimationFrame example. Do not start duplicate loops in update or resume; stop and restart one owned loop. Cancel fetches with AbortController and call unsubscribe functions returned by streaming APIs. The runtime’s automatic pause handling does not replace these responsibilities.

Dynamic Config Schema (`configSchema`)

When users click the gear icon on your widget, Wallcraft automatically renders an acrylic modal form based on your configSchema array. When saved, the updated configuration object is passed to your update(config) method.

Field Type Description Key Parameters Output Value
"text" Single-line input. If key contains image, photo, url or path (case-insensitive), Settings adds an image Browse button wired to selectImage() with its default image filter. This is not a generic file/application or folder picker; the schema exposes no picker-kind option. default string
"number" Numeric stepper input. default, min, max number
"boolean" / "toggle" Modern toggle switch. default boolean
"range" / "slider" Smooth range slider with live feedback. default, min, max, step, unit number
"color" Hex color picker palette. default: "#D4FF00" string
"select" Dropdown select list. Accepts flat array or { value, label } objects. options, default string | number
"font-family" Central Typography Picker. Automatically exposes Wallcraft's 13 font options without defining options! default: "default" string (e.g. "JetBrains Mono", "Inter")
"reorder" / "sortable" Sortable list allowing users to reorder items with live up/down buttons and reset action. items: [{ id, label }], default: ["id1", "id2"] string[] (sorted array of IDs)

Reorder Schema Specification

The reorder type renders sortable cards with up/down arrows. When reordered by the user, the update(config) lifecycle hook receives a sorted array of string IDs:

JSON & JavaScript Reorder Schema & Delivery
// 1. In manifest.json configSchema:
{
  "key": "metricOrder",
  "label": "Cards Display Order",
  "type": "reorder",
  "items": [
    { "id": "cpu", "label": "Processor (CPU)" },
    { "id": "ram", "label": "Memory (RAM)" },
    { "id": "gpu", "label": "Graphics (GPU)" }
  ],
  "default": ["cpu", "ram", "gpu"]
}

// 2. Delivered to update(config):
update: function (config) {
  // config is ALWAYS the complete merged settings object
  console.log(config.metricOrder); // ["gpu", "cpu", "ram"]
  // Update DOM order using CSS flexbox or child nodes:
  config.metricOrder.forEach((id, idx) => {
    const el = this.container.querySelector('#' + id);
    if (el) el.style.order = idx;
  });
}
labelKey (i18n) vs. label
Use label for static strings (e.g. "Custom Title"). Use labelKey to reference Wallcraft's built-in localization dictionary (e.g. "showSeconds", "accentColor") so titles translate dynamically.

Do not use the automatic image Browse button for executables, shortcuts or folders. For a manually pasted application target, choose a neutral text key such as appTarget, for example {"key":"appTarget","type":"text","label":"Application target","default":""}. The user must save the Settings form to persist the value. Alternatively, call window.WallCraft.selectAppOrFolder() from a widget user action for a session-only selection; that call does not save configuration.

Design Tokens & 11 Frame Options

Wallcraft provides a centralized Design Token Layer. By using semantic CSS variables instead of hardcoded hex values, your widget automatically adapts to the selected frame option: 10 built-in themes (including Liquid Glass, Flat Dark, and Caldera) or Zero-Wrapper.

CSS widget.css (Semantic Theme Styling)
/* Card styled with dynamic theme tokens */
.my-card {
  background: var(--wc-theme-surface);
  border: 1px solid var(--wc-theme-surface-border);
  border-radius: var(--wc-theme-radius-card);
  color: var(--wc-theme-text-main);
  font-family: var(--wc-font-primary);
}

/* Action button inheriting theme active accents */
.my-action-btn {
  background: var(--wc-theme-btn-bg);
  border: 1px solid var(--wc-theme-btn-border);
  border-radius: var(--wc-theme-radius-btn);
  color: var(--wc-theme-text-sub);
  transition: all var(--wc-trans-fast);
}

.my-action-btn:hover {
  background: var(--wc-theme-btn-hover);
  color: var(--wc-theme-text-main);
}

/* Neon primary accent */
.my-primary-badge {
  background: var(--wc-theme-primary);
  box-shadow: var(--wc-glow-accent);
  color: #000000;
  font-weight: 700;
}
Best Practice: External Font Imports Discouraged
Avoid duplicate font requests by using the shared font tokens instead of adding another @import url(...) in widget CSS. The picker has 13 options: 11 Google Fonts families, system-ui and the theme’s default. The sandbox loads Google Fonts CSS/font resources online; offline or blocked requests use the configured fallback stacks. Local availability and zero layout shift are not guaranteed. Reference font-family: var(--wc-font-mono); or var(--wc-font-primary);.

10 Built-in Themes, Frameless Mode & CSS Variables

WallCraft includes 10 frame themes and a frameless mode. The table lists their default accents and card/button radii from the theme token definitions. Flat Dark uses an opaque surface without blur or shadows. Liquid Glass uses a translucent, separately filtered glass layer; Acrylic Blur also uses backdrop blur. Users can customize fonts, colors and radii in Settings:

Theme / wrapper keyPrimary accentSecondary accentCard / button radiusVisual character
WallCraft Default
"squircle-acrylic"
#FF5A36 #FFA959 14px / 14px Smoked dark glass with layered highlights and rounded corners.
Flat Dark
"flat-dark"
#C4B5FD #B8B2C4 24px / 10px Opaque charcoal surface with lavender accents; no blur or elevation.
Liquid Glass (GPU Intensive)
"flat-light"
#365A7B #55758B 34px / 24px Translucent blue-tinted glass, double rim highlights and a separate 24px blur layer. The stored theme key remains flat-light for compatibility; resource use depends on hardware.
Cyber Obsidian
"cyber-obsidian"
#00F0FF #FF0055 8px / 6px Titanium-style dark HUD with cyan and pink accents and compact corners.
Glacial Pearl
"glacial-pearl"
#0284C7 #0EA5E9 24px / 12px Light pearl surface with icy blue accents and soft rounded corners.
Aurora Prism
"aurora-prism"
#A855F7 #22D3EE 22px / 12px Violet and cyan gradients with spectral edge highlights.
Acrylic Blur (GPU Intensive)
"acrylic-blur"
#BAE6FD #38BDF8 36px / 14px Translucent acrylic with 24px backdrop blur; resource use depends on hardware.
Caldera
"caldera-ember"
#FC5000 #524AE9 40px / 14px Warm limestone surface with orange and violet accents and broad corners.
Slash Vault
"slash-vault"
#CC9166 #B85E3A 10px / 14px Deep onyx surface with copper accents and fine ledger-like lines.
Linear Precision
"linear-precision"
#E4F222 #27A644 12px / 6px Dark carbon surface with lime and green accents and precise inset borders.
Zero-Wrapper
"none"
#D4FF00 #00F0FF 0px / 8px Transparent canvas without a wrapper border, background or card radius.

Dynamic Theme Customization

Users can tailor any wrapper theme directly in Wallcraft Settings → Theme Tokens with immediate live preview:

  • Primary & Secondary Typography: Independently select from 13 font options.
  • 2-Tier Accent Color Hierarchy: Override --theme-accent (primary) and --theme-accent-secondary (glow/badges).
  • Corner Curvature: Sliders for Card Border Radius (0px to 64px) and Button / Pill Radius (0px to 32px).
  • Live RPC Synchronization: Changes are immediately broadcasted to all active widget iframes via wc:theme-update.

The 13 Font Options

Font Key Typography Style Characteristics
default Theme-adaptive Dynamically resolves to the native font of the active wrapper theme.
Inter Neo-grotesque Sans Clean, modern, optimized for computer screens and small UI text.
Roboto Utilitarian Sans Balanced, neutral geometry with friendly geometric curves.
Outfit Geometric Display Contemporary geometric design with high visual presence.
JetBrains Mono Developer Monospace Optimized for reading code, metrics, and digital clocks.
Geist Mono Futuristic Monospace Minimalist monospace inspired by architectural precision.
Geist Neutral Sans Engineered for high readability across dense telemetry stations.
Bebas Neue Condensed Display All-caps condensed display font perfect for bold digital clocks.
Anton Poster Display Reworked traditional grotesque with ultra-bold weight.
DM Sans Low-contrast Sans Geometric sans-serif with subtle elegance for widget bodies.
Playfair Display Editorial Serif High-contrast serif typography for classic and luxury cards.
Fraunces Soft-serif Display Expressive vintage serif inspired by early 20th century typefaces.
system-ui OS Native Native Windows Segoe UI font for seamless OS integration.

Community Widget Localization (i18n)

Community widgets are standalone .zip archives. WallCraft passes the selected interface language to your widget through window.WallCraft.language and window.WallCraft.onLanguageChange. Each widget supplies its own translations.

JavaScript i18n Implementation
const I18N = {
  en: { title: "Network Monitor", download: "Download" },
  tr: { title: "Ağ Monitörü", download: "İndirme" }
};

function t(key) {
  const lang = window.WallCraft?.language || 'en';
  return I18N[lang]?.[key] || I18N['en']?.[key] || key;
}

// Listen to live language changes
window.WallCraft?.onLanguageChange?.((newLang) => {
  updateTranslations();
});

System Stats & Telemetry API (`getSystemStats`)

Public SDK: system-stats permission

Widgets with the "system-stats" permission can query real-time hardware telemetry including CPU utilization, core temperature, RAM usage, GPU load, GPU temperature (via NVIDIA NVML / AMD ADL), and system uptime.

JavaScript Hardware Telemetry Example
// Reading real-time hardware telemetry
const stats = await window.WallCraft.getSystemStats();

console.log('CPU Usage (%):', stats.cpu.usage);
console.log('CPU Temp (°C):', stats.cpu.temperature ?? stats.cpu.temp ?? '--'); // null if no sensor monitor active
console.log('CPU Model:', stats.cpu.model);
console.log('CPU Cores:', stats.cpu.cores);
console.log('RAM Usage (%):', stats.ram.usage);
console.log('RAM Total (GB):', stats.ram.total);
console.log('RAM Used (GB):', stats.ram.used);
// Raw byte telemetry is also available under stats.memory:
// stats.memory.used, stats.memory.total, stats.memory.free
console.log('GPU Usage (%):', stats.gpu.usage);
console.log('GPU Temp (°C):', stats.gpu.temperature ?? '--'); // Queried natively via NVML (NVIDIA) or ADL (AMD)
console.log('GPU Model:', stats.gpu.name);
console.log('System Uptime (sec):', stats.uptime);

Telemetry Object Schema

Property Type Description
stats.cpu.usage number Overall CPU utilization percentage (0–100).
stats.cpu.temperature number | null CPU core package temperature in °C (null if sensor unavailable).
stats.cpu.cores number Logical core count.
stats.cpu.model string Processor model brand string.
stats.gpu.usage number 3D graphics engine load percentage (0–100).
stats.gpu.temperature number | null Dedicated GPU temperature in °C via NVIDIA NVML or AMD ADL.
stats.gpu.name string Graphics adapter model name.
stats.ram.usage number System RAM utilization percentage (0–100).
stats.ram.total / used number Total and used physical memory in gigabytes.
stats.uptime number System running time in seconds since last boot.
Manifest Permission Required
You must declare "permissions": ["system-stats"] in your widget's manifest.json to access this API.
Polling Frequency Guideline (Recommended 1000ms – 2000ms)
Hardware telemetry calculation (CPU delta counters, disk I/O, GPU queries) executes native Windows kernel queries. Never poll this API in a tight loop (<500ms). We strongly recommend polling every 1000ms to 2000ms, and clearing the interval inside your pause() and destroy() lifecycle hooks.
Method / parametersReturn / cancellationPermission / cleanup
getSystemStats()Promise<object|null>; permission/bridge errors reject; unavailable sensors may lack readings.system-stats; stop your polling timers in pause/destroy.

Core Audio SDK (`window.WallCraft.audio`)

Wallcraft exposes Windows WASAPI Core Audio controls directly to sandboxed widgets through the asynchronous window.WallCraft.audio bridge.

JavaScript Audio SDK Methods
// 1. Read master volume and active process audio sessions
const state = await window.WallCraft.audio.getState();
console.log('Master Volume:', state.master.volume); // 0 - 100
console.log('Is Muted:', state.master.isMuted);     // boolean
console.log('Active Sessions:', state.sessions);    // Spotify, Chrome, etc.

// 2. Set master system volume (0 - 100)
await window.WallCraft.audio.setMasterVolume(80);

// 3. Toggle master mute
await window.WallCraft.audio.setMasterMute(true);

// 4. Control individual app volume by Process ID (PID)
await window.WallCraft.audio.setAppVolume(spotifyPid, 50);
await window.WallCraft.audio.setAppMute(spotifyPid, true);

// 5. Real-time audio change listener & peak metering
const unsubscribe = window.WallCraft.audio.onStateChange((state) => {
  // Schedule rendering; requestAnimationFrame is not a rate limiter.
  requestAnimationFrame(() => {
    updateVolumeSlider(state.master.volume); // 0 - 100
    updatePeakMeter(state.master.peak);       // 0 - 100 (real-time peak)
  });
});

// Remember to call unsubscribe() inside destroy()!
Manifest Permission Required
You must declare "permissions": ["audio"] in your widget's manifest.json to access the Windows WASAPI Core Audio mixer and session volume controls.
Rate Limiting & Rendering Performance
The mixer normally polls every 100ms with peak metering active or 1000ms otherwise; mutations/events may send extra updates. Coalesce DOM writes and skip unchanged values. requestAnimationFrame alone is not throttling or a CPU guarantee. Only the host audioMixer.onStateUpdate(callback, {peak: false}) exposes the peak subscription option; public audio.onStateChange(callback) does not.
Method / parametersReturn / cancellationPermission / cleanup
audio.getState()Promise<state>; bridge/permission failures reject.audio
audio.setMasterVolume(volume), setAppVolume(pid, volume)Promise<{success:true, ...}> acknowledges dispatch, not verified native completion. Volume is rounded/clamped to 0–100; invalid input rejects.audio
audio.setMasterMute(mute), setAppMute(pid, mute)Promise<{success:true, ...}> dispatch acknowledgement. pid must be positive; mute is converted to boolean.audio
audio.onStateChange(callback)Returns an unsubscribe function; no peak option.audio; call unsubscribe in destroy.

setAppMute needs a build containing the 13 October 2026 preload bridge fix. Older builds may reject the request; handle errors and do not display a native success guarantee based only on the dispatch acknowledgement.

Windows SMTC Media Control API

Widgets with the "media" permission can interface with Windows System Media Transport Controls (SMTC) to read metadata from Spotify, Chrome, Edge, and Apple Music, as well as trigger playback controls.

JavaScript Media Control Examples
// 1. Listen for Now Playing track metadata
window.WallCraft.onMediaInfo((mediaInfo) => {
  console.log('Track Title:', mediaInfo.title);
  console.log('Artist:', mediaInfo.artist);
  console.log('Album:', mediaInfo.album);
  console.log('Thumbnail:', mediaInfo.thumbnail); // base64 / URL
  console.log('Playback Status:', mediaInfo.status); // "playing", "paused", "stopped"
  console.log('Timeline:', mediaInfo.position, '/', mediaInfo.duration);
});

// 2. Request immediate metadata refresh
window.WallCraft.requestMediaInfo();

// 3. Media playback controls (Supported actions: 'play-pause', 'next', 'previous', 'mute', 'volume-up', 'volume-down')
window.WallCraft.mediaControl('play-pause');
window.WallCraft.mediaControl('next');
window.WallCraft.mediaControl('previous');
Manifest Permission Required
You must declare "permissions": ["media"] in your widget's manifest.json to subscribe to track metadata and dispatch media playback commands.
Method / parametersReturn / cancellationPermission / cleanup
onMediaInfo(callback)Unsubscribe function; callback receives metadata updates.media; unsubscribe in destroy.
requestMediaInfo(options), mediaControl(action)void; fire-and-forget, no completion/permission rejection Promise.media; denied calls are not executed.

Discord Voice RPC Integration API

Widgets with the "discord" permission connect directly to the Discord Gateway RPC client on Windows, allowing you to create live voice channel overlays and voice activity indicators on the desktop.

JavaScript Discord Voice RPC API
// 1. Read voice channel state
const discordState = await window.WallCraft.getDiscordVoiceState();
// Channel name: discordState.channelName or discordState.currentChannel?.name
console.log('Connected Channel:', discordState.channelName || discordState.currentChannel?.name);
// Members list: discordState.members or discordState.participants
console.log('Users in Room:', discordState.members || discordState.participants);

// 2. Speaking indicator hook ({ userId, speaking, isSpeaking })
window.WallCraft.onDiscordSpeaking(({ userId, speaking, isSpeaking }) => {
  highlightUserAvatar(userId, isSpeaking ?? speaking);
});

// 3. Room state changes
window.WallCraft.onDiscordVoiceState((newState) => {
  renderVoiceRoom(newState);
});

// 4. Mute / Deafen controls
window.WallCraft.toggleDiscordMute();
window.WallCraft.toggleDiscordDeaf();
window.WallCraft.disconnectDiscordVoice();
Manifest Permission Required
You must declare "permissions": ["discord"] in your widget's manifest.json to connect to Discord Voice RPC, inspect active channels, and toggle mute/deafen states.
Method / parametersReturn / cancellationPermission / cleanup
getDiscordVoiceState()Promise<state|null>; handle disconnected state and rejection.discord
toggleDiscordMute(), toggleDiscordDeaf(), disconnectDiscordVoice()Promise<null> after bridge dispatch; errors reject.discord
onDiscordVoiceState(cb), onDiscordSpeaking(cb)Return unsubscribe functions.discord; unsubscribe in destroy.

App Launcher & Icons API

Launch installed Windows software and extract native high-resolution icons directly from executable (.exe) and shortcut (.lnk) files:

JavaScript App Launcher & Icon Methods
// 1. Launch a desktop shortcut or executable
await window.WallCraft.launchShortcut('C:\\Program Files\\Spotify\\Spotify.exe');

// 2. Extract base64 PNG icon directly from Windows binary
const iconBase64 = await window.WallCraft.getFileIcon('C:\\Windows\\notepad.exe');
iconImageElement.src = iconBase64;

// 3. Prompt user to select an app or folder
const targetPath = await window.WallCraft.selectAppOrFolder({
  title: 'Select Application for Quick Launch'
});
Privileged Bridge & Shell Execution
Executable launching (launchShortcut) requires system privileges. In community widgets, launcher actions are mediated via the host bridge and require explicit user configuration.
Method / parametersReturn / cancellationPermission / cleanup
selectAppOrFolder(options)Promise<{path,target,name}|null>; cancel → null. Folder mode: type:"folder", folder or openDirectory.Host picker; selection is not launch authorization.
getFileIcon(path)Promise<string|null>; icon data URL or no icon.Host-resolved icon only.
launchShortcut(path)Promise<{success:boolean,error?:string}|null>; cancellation/unsafe target may resolve with success:false; bridge errors reject.launcher plus per-target security confirmation; UNC paths, command interpreters, dangerous scripts/shortcut destinations and .url shortcuts are restricted.
Launcher: cancel and error handling
async function chooseAndLaunch(sdk = window.WallCraft) {
  const selection = await sdk.selectAppOrFolder({});
  if (!selection) return {cancelled: true};
  const result = await sdk.launchShortcut(selection.path);
  if (!result?.success) throw new Error(result?.error || 'Launch failed');
  return result;
}
// From a user action: chooseAndLaunch().catch(showError);
// Requires launcher approval and a separate target confirmation.

Selection is not persistence. selectAppOrFolder() returns a selection only; it does not write widget configuration. The public sandbox SDK currently has no config-write method. Assigning the selected path to a widget variable or WallCraft.config lasts only for the current instance and can be replaced by a Settings update or reload. To retain a target, expose a neutral appTarget text field through configSchema and have the user paste the target and save the Settings form. The in-memory storage shim is not a persistence mechanism.

Native File Pickers & Wallpaper Path API

Access Windows native file picker dialogs, retrieve current system wallpaper path. These calls are mediated by the host; they do not grant direct filesystem access:

JavaScript File Pickers & Wallpaper Path
// 1. Select image or wallpaper file
const imagePath = await window.WallCraft.selectImage({ title: 'Select Background Image' });

// 2. Select video file
const videoPath = await window.WallCraft.selectVideo({ title: 'Select Video File' });

// 3. Get currently active Windows desktop wallpaper path
const wallpaperPath = await window.WallCraft.getWindowsWallpaperPath();
Method / parametersReturn / cancellationPermission / cleanup
selectImage(options), selectVideo(options)Promise<string|string[]|null>; single cancel → null, multi cancel → []. Image directory mode returns image paths or [].No filesystem grant; host dialog only. Use multiple/multiSelections; image folder mode uses folder/directory/openDirectory.
getWindowsWallpaperPath()Promise<string|null>; null when not available.No capability permission. This is a host-resolved path, not file-read access.

Clipboard Access API (writeClipboardText)

Privileged Host API (Preload Layer)

Write plain text programmatically to the Windows system clipboard. While community widgets use synthetic keyboard shortcuts (Ctrl+C, Ctrl+V) and postMessage clipboard signals transparently, privileged host components and settings windows can trigger clipboard writes directly:

JavaScript window.wallcraft.writeClipboardText
// Programmatically write text to Windows clipboard
window.wallcraft.writeClipboardText('https://getwallcraft.com');
console.log('Copied text to Windows clipboard successfully!');
Community Widget Clipboard Support
Sandboxed community widgets do not need direct clipboard permissions. When a user selects text in an <input>, <textarea>, or editable element and presses Ctrl+C or Ctrl+X, the runtime dispatches a wc:clipboard-copy postMessage event to the host, automatically populating the Windows clipboard.

Diagnostics & Feedback API

Privileged Host API (Preload Layer)

Used primarily by Wallcraft settings and troubleshooting panels to inspect GPU capabilities, memory limits, and submit in-app developer reports:

JavaScript Diagnostics & Feedback Methods
// 1. Retrieve full system and GPU diagnostics
const diag = await window.wallcraft.getSystemDiagnostics();
console.log('GPU Renderer:', diag.gpu.renderer);
console.log('VRAM Dedicated (MB):', diag.gpu.dedicatedMemory);
console.log('Electron / Chrome Version:', diag.versions.electron, diag.versions.chrome);
console.log('OS Build:', diag.os.release);

// 2. Submit user feedback or crash log
await window.wallcraft.submitFeedback({
  type: 'bug', // 'bug', 'feature', 'general'
  subject: 'Shader compile failed on Intel Iris',
  message: 'Detailed description of the issue...',
  includeDiagnostics: true
});

// 3. Listen for requests to open the feedback modal dialog
window.wallcraft.onOpenFeedbackModal(() => {
  openFeedbackUI();
});

Widget Management & Package API

Privileged Host API (Preload Layer)

Powers the Wallcraft Settings UI (Modules & Widgets tab) to catalog installed widgets, safely read source files, open the community widgets directory, import packages, and delete widgets:

JavaScript Host Widget Management Methods
// 1. Retrieve all installed built-in and community widgets
const widgets = await window.wallcraft.getAvailableWidgets();
console.log('Available Widgets:', widgets);

// 2. Safely read a widget source file (e.g. manifest.json, widget.js)
const manifestContent = await window.wallcraft.readWidgetFile('my-clock-widget', 'manifest.json');

// 3. Open user-widgets directory in Windows File Explorer
await window.wallcraft.openWidgetsFolder();

// 4. Import a widget (.zip or folder mode)
const importResult = await window.wallcraft.importWidget('zip'); // 'zip' or 'folder'
console.log('Imported Widget ID:', importResult.widgetId);

// 5. Delete a community widget from disk
await window.wallcraft.deleteWidget('my-old-widget');

// 6. Listen for changes when widgets are imported or removed
window.wallcraft.onWidgetsUpdated((updatedList) => {
  refreshWidgetCatalog(updatedList);
});
Normalized Media Metadata in getAvailableWidgets()
Each manifest object returned by getAvailableWidgets() is automatically augmented with detected icon, iconImage, and previewImage absolute file:/// URLs (or remote HTTPS URLs for marketplace items), fully processed through detectWidgetMedia. The Settings UI consumes these resolved paths directly to render widget tile icons and fallbacks without manual path resolution.

Declarative Modals RPC (`window.WallCraft.showModal`)

Due to iframe sandbox policies, widgets cannot spawn unconstrained popups. Instead, Wallcraft provides a sleek, acrylic desktop modal dialog triggered via a single asynchronous promise call:

JavaScript window.WallCraft.showModal
const result = await window.WallCraft.showModal({
  title: "Create Reminder",
  description: "Enter reminder details for your desktop.",
  confirmText: "Save Note",
  cancelText: "Cancel",
  width: 420,
  fields: [
    {
      id: "title",
      type: "text",
      label: "Note Title",
      placeholder: "Meeting with team..."
    },
    {
      id: "priority",
      type: "select",
      label: "Priority",
      options: [
        { value: "low", label: "Low" },
        { value: "high", label: "High" }
      ],
      default: "high"
    },
    {
      id: "pinToTop",
      type: "toggle",
      label: "Pin to Top",
      default: true
    }
  ]
});

if (result && result.confirmed) {
  console.log("Form Values:", result.values);
  // result.values = { title: "...", priority: "high", pinToTop: true }
}

Desktop Input & Synthetic Keyboard Bridge

Wallcraft routes native Windows mouse and keyboard events directly into the sandboxed iframe:

  • Pointer Simulation: Desktop clicks are translated to PointerEvent inside the sandbox.
  • Keyboard Hook: Focusing an <input> or <textarea> activates a transparent Win32 keyboard hook for typing, Backspace, Enter, and Arrow keys.
  • Clipboard Sync: Ctrl+C, Ctrl+V, and Ctrl+X sync seamlessly with the Windows clipboard over postMessage.

Storage & State Management (In-Memory Shim)

HTML5 sandboxed iframes without allow-same-origin cannot use normal origin-backed browser storage. WallCraft installs an In-Memory Storage Shim for localStorage and sessionStorage. Both use the same Map within the current iframe instance; values are lost when the widget reloads or its iframe is recreated. This is temporary storage, not disk-backed persistence.

For persistent preferences, declare fields with configSchema in manifest.json and have the user save them through the Settings form. Declaring the schema or calling selectAppOrFolder() alone does not save values. The public sandbox SDK has no config-write method; changing a local variable, WallCraft.config or the storage shim does not update saved Settings.

Packaging & Distribution (.zip)

To prepare a widget ZIP for local sharing or import:

  1. Package Structure: Ensure manifest.json is located at the root of the archive (not nested in an extra subfolder).
  2. Media Assets: Include a dedicated tile icon (icon.png or icon.webp, 1:1 square ratio, recommended 128×128 or 256×256 px) and a high-resolution preview image (preview.webp or preview.png, 16:9 ratio, recommended 1280×720 or 1920×1080 px) at the archive root for instant visual recognition in the Wallcraft catalog.
  3. Flat Compression: Compress the folder contents into a standard .zip file (e.g. my-widget.zip).
  4. Local Validation: Test extraction by importing the .zip via Wallcraft Settings -> Modules & Widgets -> "Import Package".
Limit / checkValue
Individual file5 MiB
Total extracted content64 MiB
ZIP archive32 MiB
Archive entries1024
Per-file compression ratio≤ 200
Rejected pathsUnsafe/reserved names, traversal, absolute paths, case-insensitive duplicates and symlinks.

For import errors, check the root manifest, ID/path safety and every size limit first. Do not ship caches, node_modules or build tooling in the widget ZIP. Test the exact archive you intend to share.

Publishing to Community Hub

  1. Publication identity: Open the in-app Community Hub submission modal and select your own saved shader, widget or preset. Display names may repeat across all types; catalog IDs must be unique across types, ignoring case. Creator Studio generates a publication ID independently of the title. For updates, select the same saved local source to retain that ID, even when renaming the item. Save an unsaved creation locally before its first submission if you plan to update it; an unsaved draft ID is retained only while the modal stays open. Hub downloads cannot be re-published.
  2. Export formats: Use Export Package in the submission modal after filling in metadata. Widgets export as .zip, shaders as .json, and presets as .wallcraft-preset.json. Only widget ZIPs use a root manifest.json. Keep the generated ID; do not derive it from the display name or export the original local source instead.
  3. Submit: Use Submit PR to Hub with a classic GitHub token with public_repo scope, or use the modal’s Git commands inside a local clone of your fork after installing Git/GitHub CLI and running gh auth login. For manual submission, extract the widget ZIP contents into widgets/<publication-id>/; save shader JSON as shaders/<publication-id>.json and preset JSON as presets/<publication-id>.wallcraft-preset.json. Preserve manifest.id, instanceName and asset paths. A JSON payload containing widgetFiles is not a widget manifest; use the exported ZIP.
  4. Validation and dependencies: Widget manifest IDs and shader/preset payload IDs must match their catalog IDs. Widget pack child IDs must also be unique and must not collide with catalog items or built-in widget IDs. Publish custom widgets and shaders used by a preset first, then export the preset from the submission modal so known publication IDs are used in its references. Presets do not embed those widget packages. Maintainers regenerate catalog-v2.json with commit pins and SHA-256 hashes after reviewing source changes; contributors do not hand-edit hashes.

Multi-Monitor & Per-Monitor Profiles

Wallcraft provides a resilient multi-monitor hardware identity engine (monitor-identity.js) that decouples transient OS display indexes from physical monitors.

Core Architectural Features

  • Persistent Hardware Identity: Resolves displays by EDID descriptors, PnP manufacturer IDs, physical resolution, and scale factor. Disconnecting or reconnecting a monitor preserves saved positions and layouts without corruption.
  • Independent Per-Monitor Customization: Each physical monitor can run its own independent live wallpaper (different GLSL shaders, videos, image slideshows, or solid colors), its own independent set of widgets on separate 24×24 magnetic grids, and individual theme token overrides.
  • Display Identify Overlays: The showDisplayIdentifyOverlays service flashes high-contrast monitor numbers directly onto each physical screen, allowing users to immediately identify which display is being configured in Settings.
  • Multi-Profile Management: Create and switch between up to 10 persistent user profiles with inline rename, duplicate, and delete actions. Profiles can be exported and shared as .json preset files.

Always-On-Top Pinned Widgets (Overlay Mode)

In addition to standard desktop-level widgets behind application windows, Wallcraft supports Always-on-Top Pinned Overlays that float above games, full-screen apps, and browser windows.

How Pinned Overlays Operate

  • WS_EX_TOPMOST Management: Pinned widgets run inside specialized transparent overlay windows (pinnedOverlayWindows).
  • Foreground WinEvent Hook: A native EVENT_SYSTEM_FOREGROUND hook automatically re-asserts pinned widgets at the head of the HWND_TOPMOST band whenever games, browsers, or external windows gain focus.
  • Transparent Click-Through Hit Testing: When the mouse is outside a pinned widget's boundary, the window applies setIgnoreMouseEvents(true): clicks pass completely through to the game or application below. When the cursor enters widget bounds or an interactive modal opens, setIgnoreMouseEvents(false) is dynamically engaged for instant interactivity.

3D Shader Engine: WebGL 2.0 Pipeline

Wallcraft runs on a high-performance WebGL 2.0 (with WebGL 1.0 fallback) fullscreen quad. Your custom GLSL fragment shader paints directly to the GPU at 60/144+ FPS.

GLSL Minimal Shader Template
// Wallcraft automatically injects precision, u_resolution, u_time, and v_uv.
void main() {
  vec2 uv = (gl_FragCoord.xy * 2.0 - u_resolution.xy) / min(u_resolution.x, u_resolution.y);

  float d = length(uv) - 0.5;
  vec3 col = vec3(sin(d * 10.0 + u_time), cos(d * 8.0 - u_time), 1.0);

  gl_FragColor = vec4(col, 1.0);
}

Complete Uniforms Reference

Uniform Name GLSL Type Range / Unit Description
u_resolution vec2 (width, height) Current screen viewport resolution in physical pixels.
u_time float 0.0 -> ∞ (sec) Continuous monotonic running time since shader start.
u_deltaTime float ~0.001 -> 0.1 (sec) Elapsed delta time between consecutive frames (for physics & motion).
u_frame int 0, 1, 2, ... Monotonic total rendered frame count.
v_uv varying vec2 [0.0, 1.0] Normalized UV coordinates (Bottom-left: 0,0, Top-right: 1,1).
u_mouse vec2 [0.0, 1.0] Current normalized mouse coordinates across the desktop.
u_mouse_click vec2 [0.0, 1.0] Coordinates of the most recent mouse click event.
u_audio_vol float [0.0, 1.0] Standalone capture: average normalized FFT magnitude across 32 bins, not RMS or the OS volume setting.
u_audio_bass float [0.0, 1.0] Standalone: average of bins 0–3; not a fixed 20–150 Hz band.
u_audio_mid float [0.0, 1.0] Standalone: average of bins 4–15; frequency boundaries depend on sample rate.
u_audio_treble float [0.0, 1.0] Standalone: average of bins 16–31; visualizer mappings may differ.
u_audio_spectrum[32] float[32] [0.0, 1.0] 32 normalized spectrum values. Standalone capture uses linear FFT bins; the visualizer feed can use a different mapping.
u_color1, u_color2, u_accent vec3 rgb [0.0, 1.0] Configured primary, secondary, and accent colors from Settings.
u_speed float 0.1 -> 5.0 Shader animation speed multiplier from Settings.
u_intensity float 0.1 -> 5.0 Visual intensity or bloom multiplier from Settings.
u_texture / u_channel0 sampler2D Texture Unit 0 User-selected custom wallpaper texture or video frame.
u_noise / u_channel1 sampler2D Texture Unit 1 Built-in 256×256 seamless smooth value noise texture.
u_prev_frame / u_channel2 sampler2D Texture Unit 2 Previous frame FBO texture for feedback and fluid simulations.
u_channel3 sampler2D Texture Unit 3 Secondary texture or multi-pass buffer channel.
u_has_texture int 0 or 1 1 if custom user texture is loaded; 0 otherwise.

32-Value Audio Reactivity

Standalone shader capture uses desktop getUserMedia, an AudioContext and AnalyserNode with fftSize = 64. A requestAnimationFrame loop reads 32 FFT bins and divides byte magnitudes by 255. Capture availability and latency depend on the device and runtime; this path does not promise a worker or zero latency. A running Audio Visualizer can feed the shader through a different spectrum mapping.

Access individual frequency bands in your shader with u_audio_spectrum[i] (where i is 0 to 31), or use convenient grouped bands: u_audio_bass, u_audio_mid, and u_audio_treble.

Auto-Detected Uniform Annotations

Wallcraft automatically parses custom uniforms in your GLSL code and produces interactive sliders, color pickers, and toggles directly in the Settings UI:

GLSL Custom Dynamic Uniforms
// 1. Float Slider with custom ranges
uniform float u_waveSpeed = 2.5;   // min: 0.1, max: 10.0, step: 0.1, label: Wave Speed
uniform int   u_particles = 100;   // min: 10, max: 500, label: Particle Count

// 2. Vector Parameters (2D / 4D Sliders)
uniform vec2  u_scale;             // default: [1.0, 1.0], min: 0.1, max: 5.0, label: Zoom

// 3. Color Picker
uniform vec3  u_glowColor;         // default: #00ffcc, label: Glow Color

// 4. Boolean Switch
uniform bool  u_enableBloom;       // default: true, label: Enable Bloom

Universal GLSL Compatibility

Wallcraft features an intelligent compilation layer. You can use standard GLSL 2.0 fragment shaders with zero manual rewriting:

  • void mainImage(out vec4 fragColor, in vec2 fragCoord) is automatically wrapped into standard void main().
  • iResolution, iTime, iTimeDelta, iFrame, and iMouse are natively supported.
  • iChannel0 through iChannel3 map directly to Wallcraft texture channels.

Multi-Pass Pipeline, Ping-Pong FBO & Procedural Textures

When your shader references the u_prev_frame uniform, Wallcraft switches into ping-pong Framebuffer Object (FBO) mode. Furthermore, Wallcraft features full multi-pass Shadertoy pipeline execution:

  • Buffer Passes: Full native support for Common, Buffer A, Buffer B, Buffer C, Buffer D, and the final Image rendering passes.
  • Flexible Channel Routing: Route any buffer's output to another buffer's iChannel0..3 input to create complex fluid dynamics, Navier-Stokes fluid advection, smoke dissipation, reaction-diffusion patterns, or optical feedback loops.
  • Built-in Procedural Textures: Bind procedural texture generators directly into channels without external image files: Smooth Value Noise, RG Noise, Rock / Stone Surface, Lichen / Moss Pattern, Wood Grain, Deep Space Stars, and Pebbles Mosaic.

Performance & Resource Cleanup

Because Wallcraft runs 24/7 on the user desktop, community widgets must never leak memory or spin CPU cycles while idle:

  1. Clean Timers & Listeners: Always clear setInterval, requestAnimationFrame, and window.WallCraft.audio.onStateChange callbacks inside your destroy() method.
  2. Pause/resume ownership: When runtime pause is active, timer/rAF callbacks are filtered and tracked audio contexts are suspended. An interval is not removed just because its callback is skipped. Recursive rAF/timeout chains need an explicit restart in resume(). Stop your loops in pause() and clean subscriptions/resources in destroy(); automatic handling is not a zero-CPU or leak-free guarantee.
  3. Avoid DOM Thrashing: Do not set textContent or style if values have not changed from the prior frame.
widget.js — restartable animation
window.WallCraftWidget = new class {
  init(container) { this.container = container; this.resume(); }
  frame = (time) => {
    this.container.style.opacity = String(0.75 + 0.25 * Math.sin(time / 1000));
    this.raf = requestAnimationFrame(this.frame);
  };
  pause() { cancelAnimationFrame(this.raf); this.raf = null; }
  resume() { this.pause(); this.raf = requestAnimationFrame(this.frame); }
  destroy() { this.pause(); this.container.style.opacity = ''; }
};