CopilotKit logo

Skill

Advanced Visualization Techniques

create advanced generative UI visualizations

Covers Dashboards Generative Art UI Components Frontend Design

Description

UI mockups, dashboards, advanced interactivity, generative art, simulations, math visualizations, and design system rules for producing rich generateSandboxedUi output.

SKILL.md

Agent Visualization Skills — Volume 2: Advanced Techniques

Prerequisite: Volume 1 (SVG diagrams, basic interactive widgets, Chart.js, Mermaid). This volume covers: UI mockups, dashboards, advanced interactivity, generative art, simulations, math visualizations, and the design system that ties everything together.


Part 0: The Tool Contract — generateSandboxedUi

Everything in this volume ships through the generateSandboxedUi tool. The UI streams as you generate it, so emit the parameters in this EXACT order:

  1. initialHeight — estimated height of the finished UI in px.
  2. placeholderMessages — 2-4 short, playful progress messages.
  3. css — ALL styles, up front. The user sees a placeholder until css is complete, so keep it lean and put every style here for the css-first reveal.
  4. html — clean body markup, streamed in live. No <style> blocks (the css parameter owns all styles), no monolithic inline <script> blocks.
  5. jsFunctions — named function declarations: the reusable toolbox of behavior.
  6. jsExpressions — small statements invoking those functions, applied one-by-one so the user watches each take effect.

Write parameterized generators in jsFunctions (drawWing(color), not drawRedWing()). A well-parameterized toolbox lets a later refinement turn — "make the wings red" — append ONE new expression to jsExpressions instead of regenerating the whole document.

The sandbox iframe has NO same-origin access: no localStorage, sessionStorage, cookies, IndexedDB, or same-origin fetch. The host bridge is await Websandbox.connection.remote.sendPrompt({ text }) and await Websandbox.connection.remote.openLink({ url }) (https only). The design system (Part 1) and an importmap for three, gsap, d3, and chart.js (Part 7) are pre-injected.


Part 1: The Design System

Every visual you produce should feel native to the host interface — not like an embedded iframe from somewhere else. These rules apply to ALL output types.

CSS Variables (Auto Light/Dark Mode)

/* Backgrounds */
--color-background-primary    /* white in light, near-black in dark */
--color-background-secondary  /* surface cards */
--color-background-tertiary   /* page background */
--color-background-info       /* blue tint */
--color-background-danger     /* red tint */
--color-background-success    /* green tint */
--color-background-warning    /* amber tint */

/* Text */
--color-text-primary          /* main text */
--color-text-secondary        /* muted / labels */
--color-text-tertiary         /* hints / placeholders */
--color-text-info / -danger / -success / -warning

/* Borders */
--color-border-tertiary       /* default: 0.15 alpha */
--color-border-secondary      /* hover: 0.3 alpha */
--color-border-primary        /* active: 0.4 alpha */

/* Typography */
--font-sans                   /* default body font */
--font-serif                  /* editorial / blockquote only */
--font-mono                   /* code */

/* Layout */
--border-radius-md            /* 8px - most elements */
--border-radius-lg            /* 12px - cards */
--border-radius-xl            /* 16px - large containers */

Critical rule: Never hardcode colors like #333 or #fff in HTML. They break in the opposite mode. Always use CSS variables.

Typography Rules

  • h1 = 22px, h2 = 18px, h3 = 16px — all font-weight: 500
  • Body = 16px, weight 400, line-height: 1.7
  • Only two weights: 400 (regular) and 500 (medium). Never 600 or 700.
  • Sentence case everywhere. Never Title Case or ALL CAPS.
  • No mid-sentence bolding. Use code style for entity/class/function names.
  • No font-size below 11px anywhere.

Component Tokens

  • Borders: 0.5px solid var(--color-border-tertiary)
  • Cards: background: var(--color-background-primary), border: 0.5px solid var(--color-border-tertiary), border-radius: var(--border-radius-lg), padding: 1rem 1.25rem
  • No gradients, drop shadows, blur, glow, or neon effects
  • No emoji — use CSS shapes or SVG paths for icons
  • Background of outer container is always transparent

Number Formatting

Always round displayed numbers. JavaScript float math leaks artifacts: 0.1 + 0.2 = 0.30000000000000004. Every number on screen must go through Math.round(), .toFixed(n), or Intl.NumberFormat.


Part 2: UI Mockups

For when the user asks you to design or prototype a UI.

When to Use

  • "Design a settings page for..."
  • "Mock up a dashboard"
  • "What should this form look like?"
  • "Show me a card layout for..."
  • Prototyping before building

Presentation Rules

Contained mockups (mobile screens, modals, chat threads, single cards): Wrap in a background surface so they don't float naked:

<div style="background: var(--color-background-secondary);
            border-radius: var(--border-radius-lg);
            padding: 2rem; display: flex; justify-content: center;">
  <!-- Your mockup inside -->
</div>

Full-width mockups (dashboards, settings pages, data tables): No wrapper needed — they naturally fill the viewport.

Where the styles go: repeated patterns become classes in the css parameter; the html parameter stays clean markup. The metric cards below model the translation — the remaining patterns in this part are shown with inline style attributes for compactness, and you should lift them into css-parameter classes the same way.

Metric Cards (for dashboards)

css parameter:

.metric-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
  gap: 12px;
  margin-bottom: 1.5rem;
}
.metric-card {
  background: var(--color-background-secondary);
  border-radius: var(--border-radius-md);
  padding: 1rem;
}
.metric-label {
  font-size: 13px;
  color: var(--color-text-secondary);
  margin-bottom: 4px;
}
.metric-value { font-size: 24px; font-weight: 500; }

html parameter:

<div class="metric-grid">
  <div class="metric-card">
    <div class="metric-label">Total revenue</div>
    <div class="metric-value">$142,800</div>
  </div>
  <div class="metric-card">
    <div class="metric-label">Active users</div>
    <div class="metric-value">8,421</div>
  </div>
</div>

Contact / Data Record Card

<div style="background: var(--color-background-primary);
            border-radius: var(--border-radius-lg);
            border: 0.5px solid var(--color-border-tertiary);
            padding: 1rem 1.25rem;">

  <div style="display: flex; align-items: center; gap: 12px;
              margin-bottom: 16px;">
    <!-- Avatar circle with initials -->
    <div style="width: 44px; height: 44px; border-radius: 50%;
                background: var(--color-background-info);
                display: flex; align-items: center; justify-content: center;
                font-weight: 500; font-size: 14px;
                color: var(--color-text-info);">JD</div>
    <div>
      <p style="font-weight: 500; font-size: 15px; margin: 0;">Jane Doe</p>
      <p style="font-size: 13px; color: var(--color-text-secondary);
                margin: 0;">Lead Engineer</p>
    </div>
  </div>

  <div style="border-top: 0.5px solid var(--color-border-tertiary);
              padding-top: 12px;">
    <table style="width: 100%; font-size: 13px;">
      <tr>
        <td style="color: var(--color-text-secondary); padding: 4px 0;">
          Email</td>
        <td style="text-align: right; padding: 4px 0;
                   color: var(--color-text-info);">jane@company.com</td>
      </tr>
    </table>
  </div>
</div>

Badges and Status Pills

<!-- Status badge -->
<span style="display: inline-block; font-size: 12px; padding: 4px 12px;
             border-radius: var(--border-radius-md);
             background: var(--color-background-success);
             color: var(--color-text-success);">Active</span>

<!-- Featured accent (the ONLY case where 2px border is allowed) -->
<div style="border: 2px solid var(--color-border-info);
            border-radius: var(--border-radius-lg);
            padding: 1rem 1.25rem;">
  <span style="font-size: 12px; padding: 4px 12px;
               border-radius: var(--border-radius-md);
               background: var(--color-background-info);
               color: var(--color-text-info);">Most popular</span>
</div>

Form Elements

Inputs, selects, textareas, buttons, and range sliders are pre-styled in the host environment. Write bare tags — they inherit correct styling:

  • Text inputs: 36px height, hover/focus states built in
  • Range sliders: 4px track + 18px thumb
  • Buttons: transparent bg, 0.5px border, hover/active states

Never use <form> tags. Use onClick / onChange handlers directly.

Comparison Cards

For "help me choose between X and Y":

<div style="display: grid; grid-template-columns:
            repeat(auto-fit, minmax(160px, 1fr)); gap: 12px;">

  <div style="background: var(--color-background-primary);
              border: 0.5px solid var(--color-border-tertiary);
              border-radius: var(--border-radius-lg);
              padding: 1rem 1.25rem;">
    <h3 style="font-size: 16px; font-weight: 500; margin: 0 0 8px;">
      Option A</h3>
    <p style="font-size: 13px; color: var(--color-text-secondary);
              margin: 0;">Description here</p>
  </div>

  <!-- Repeat for Option B, C... -->
</div>

Part 3: Advanced Interactive Widgets

Simulations and Physics

For teaching physics, algorithms, or systems behavior with real-time updates.

Pattern: Animation Loop with Controls

css parameter:

.sim-controls {
  display: flex; align-items: center; gap: 16px;
  margin: 12px 0; font-size: 13px;
  color: var(--color-text-secondary);
}
#sim {
  width: 100%; height: 300px;
  border-radius: var(--border-radius-md);
  background: var(--color-background-secondary);
}

html parameter:

<canvas id="sim"></canvas>

<div class="sim-controls">
  <button onclick="toggleSim()">Play / Pause</button>
  <label>Speed
    <input type="range" min="1" max="10" value="5" id="speed"
           oninput="setSimSpeed(+this.value)">
  </label>
  <button onclick="resetSim()">Reset</button>
</div>

jsFunctions parameter:

function initSim(count) {
  const canvas = document.getElementById('sim');
  canvas.width = canvas.offsetWidth;
  canvas.height = canvas.offsetHeight;
  window.sim = {
    canvas,
    ctx: canvas.getContext('2d'),
    running: true,
    speed: 5,
    particles: Array.from({ length: count }, () => ({
      x: Math.random() * canvas.width,
      y: Math.random() * canvas.height,
      vx: (Math.random() - 0.5) * 2,
      vy: (Math.random() - 0.5) * 2
    }))
  };
}

function stepSim() {
  const { canvas, ctx, particles, speed, running } = window.sim;
  ctx.clearRect(0, 0, canvas.width, canvas.height);
  for (const p of particles) {
    p.x += p.vx * speed * 0.2;
    p.y += p.vy * speed * 0.2;
    if (p.x < 0 || p.x > canvas.width) p.vx *= -1;
    if (p.y < 0 || p.y > canvas.height) p.vy *= -1;
    ctx.beginPath();
    ctx.arc(p.x, p.y, 4, 0, Math.PI * 2);
    ctx.fillStyle = '#534AB7';
    ctx.fill();
  }
  if (running) requestAnimationFrame(stepSim);
}

function setSimSpeed(value) { window.sim.speed = value; }

function toggleSim() {
  window.sim.running = !window.sim.running;
  if (window.sim.running) stepSim();
}

function resetSim() {
  const wasRunning = window.sim.running;
  initSim(window.sim.particles.length);
  if (!wasRunning) stepSim();
}

jsExpressions parameter:

initSim(50);
stepSim();

Math Visualizations

For plotting functions, showing geometric relationships, or exploring equations.

Pattern: Function Plotter with SVG

css parameter:

.plot-controls {
  display: flex; gap: 16px; align-items: center;
  margin: 12px 0; font-size: 13px;
  color: var(--color-text-secondary);
}
.plot-controls input[type="number"] { width: 60px; }
.plot-controls input[type="range"] { flex: 1; }

html parameter:

<svg id="plot" width="100%" viewBox="0 0 680 400">
  <!-- Grid -->
  <line x1="60" y1="200" x2="640" y2="200"
        stroke="var(--color-border-tertiary)" stroke-width="0.5"/>
  <line x1="340" y1="20" x2="340" y2="380"
        stroke="var(--color-border-tertiary)" stroke-width="0.5"/>
  <!-- Axes labels -->
  <text x="645" y="196" font-size="12"
        fill="var(--color-text-tertiary)">x</text>
  <text x="345" y="16" font-size="12"
        fill="var(--color-text-tertiary)">y</text>
  <!-- Function path drawn by JS -->
  <path id="fn-path" fill="none" stroke="#534AB7" stroke-width="2"/>
</svg>

<div class="plot-controls">
  <label>f(x) = sin(
    <input type="number" id="freq" value="1" min="0.1" max="10" step="0.1"
           oninput="plotFn()">x)
  </label>
  <label>Amplitude
    <input type="range" id="amp" min="0.1" max="3" value="1" step="0.1"
           oninput="plotFn()">
  </label>
</div>

jsFunctions parameter:

function plotFn() {
  const freq = +document.getElementById('freq').value;
  const amp = +document.getElementById('amp').value;
  const xMin = -5, xMax = 5, yMin = -3, yMax = 3;
  const toSvgX = x => 60 + (x - xMin) / (xMax - xMin) * 580;
  const toSvgY = y => 20 + (yMax - y) / (yMax - yMin) * 360;
  let d = '';
  for (let px = 0; px <= 580; px++) {
    const x = xMin + px / 580 * (xMax - xMin);
    const y = amp * Math.sin(freq * x);
    d += (px === 0 ? 'M' : 'L') + toSvgX(x).toFixed(1)
       + ' ' + toSvgY(y).toFixed(1);
  }
  document.getElementById('fn-path').setAttribute('d', d);
}

jsExpressions parameter:

plotFn();

Sortable / Filterable Data Tables

css parameter:

.data-table { width: 100%; border-collapse: collapse; font-size: 14px; }
.data-table th {
  text-align: left; padding: 8px 12px; font-weight: 500;
  border-bottom: 0.5px solid var(--color-border-secondary);
  color: var(--color-text-secondary); cursor: pointer;
  user-select: none; font-size: 12px;
}
.data-table th:hover { color: var(--color-text-primary); }
.data-table td {
  padding: 8px 12px;
  border-bottom: 0.5px solid var(--color-border-tertiary);
}
.table-filter { width: 100%; margin-bottom: 12px; }
.status-pill {
  font-size: 12px; padding: 2px 10px;
  border-radius: var(--border-radius-md);
}
.status-pill.active {
  background: var(--color-background-success);
  color: var(--color-text-success);
}
.status-pill.paused {
  background: var(--color-background-warning);
  color: var(--color-text-warning);
}

html parameter:

<input type="text" class="table-filter" placeholder="Filter..."
       oninput="filterTable(this.value)">

<table class="data-table" id="table">
  <thead>
    <tr>
      <th onclick="sortTable(0)">Name</th>
      <th onclick="sortTable(1)">Value</th>
      <th onclick="sortTable(2)">Status</th>
    </tr>
  </thead>
  <tbody id="tbody">
    <!-- Rows populated by JS -->
  </tbody>
</table>

jsFunctions parameter:

function initTable(rows) {
  window.tableData = rows;
  window.sortCol = -1;
  window.sortAsc = true;
  renderRows(rows);
}

function renderRows(rows) {
  document.getElementById('tbody').innerHTML = rows.map(r =>
    `<tr><td>${r[0]}</td><td>${r[1]}</td>
     <td><span class="status-pill ${r[2] === 'Active' ? 'active' : 'paused'}">${r[2]}</span>
     </td></tr>`
  ).join('');
}

function sortTable(col) {
  window.sortAsc = window.sortCol === col ? !window.sortAsc : true;
  window.sortCol = col;
  const asc = window.sortAsc;
  window.tableData.sort((a, b) => {
    if (a[col] < b[col]) return asc ? -1 : 1;
    if (a[col] > b[col]) return asc ? 1 : -1;
    return 0;
  });
  renderRows(window.tableData);
}

function filterTable(q) {
  const low = q.toLowerCase();
  renderRows(window.tableData.filter(r =>
    r.some(c => String(c).toLowerCase().includes(low))));
}

jsExpressions parameter:

initTable([
  ['Alpha', 42, 'Active'],
  ['Beta', 18, 'Paused'],
  ['Gamma', 91, 'Active'],
]);

Part 4: Chart.js — Advanced Patterns

Dark Mode Awareness

const isDark = window.matchMedia('(prefers-color-scheme: dark)').matches;
const textColor = isDark ? '#c2c0b6' : '#3d3d3a';
const gridColor = isDark ? 'rgba(255,255,255,0.08)' : 'rgba(0,0,0,0.06)';
const tooltipBg = isDark ? '#2C2C2A' : '#fff';

Canvas cannot read CSS variables — always detect dark mode and use hardcoded hex values.

Wrapper Pattern (Critical for Sizing)

<div style="position: relative; width: 100%; height: 300px;">
  <canvas id="chart"></canvas>
</div>
  • Height goes on the wrapper div ONLY, never on canvas.
  • Always set responsive: true, maintainAspectRatio: false.
  • For horizontal bar charts: height = (bars x 40) + 80 pixels.

Custom Legend (Always Use This)

Disable Chart.js default legend and build HTML:

plugins: { legend: { display: false } }
<div style="display: flex; flex-wrap: wrap; gap: 16px;
            margin-bottom: 8px; font-size: 12px;
            color: var(--color-text-secondary);">
  <span style="display: flex; align-items: center; gap: 4px;">
    <span style="width: 10px; height: 10px; border-radius: 2px;
                 background: #534AB7;"></span>Series A — 65%
  </span>
  <span style="display: flex; align-items: center; gap: 4px;">
    <span style="width: 10px; height: 10px; border-radius: 2px;
                 background: #0F6E56;"></span>Series B — 35%
  </span>
</div>

Dashboard Layout

Metric cards on top -> chart below -> drill-down buttons wired to the sendPrompt bridge (Part 6):

<!-- Metric cards grid -->
<div style="display: grid;
            grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
            gap: 12px; margin-bottom: 1.5rem;">
  <!-- cards here -->
</div>

<!-- Chart (no card wrapper) -->
<div style="position: relative; width: 100%; height: 300px;">
  <canvas id="chart"></canvas>
</div>

Chart Type Selection Guide

Data patternChart type
Trend over timeLine
Category comparisonVertical bar
Ranking (few items)Horizontal bar
Part of wholeDoughnut
DistributionHistogram (bar)
Correlation (2 variables)Scatter
Multi-variable comparisonRadar
Range / uncertaintyLine with fill area

Part 5: Generative Art and Illustration

For when the user asks for something creative, decorative, or aesthetic.

When to Use

  • "Draw me a sunset" / "Create a pattern"
  • Decorative headers or visual breaks
  • Mood illustrations for creative writing
  • Abstract visualizations of data or music

Rules (Different from Diagrams)

  • Fill the canvas — art should feel rich, not sparse
  • Bold colors are encouraged. You can use custom hex freely.
  • Layered overlapping shapes create depth
  • Organic forms with <path> curves, <ellipse>, <circle>
  • Texture via repetition (hatching, dots, parallel lines)
  • Geometric patterns with <g transform="rotate()">
  • NO gradients, shadows, blur, or glow (still flat aesthetic)

Pattern: Geometric Art

<svg width="100%" viewBox="0 0 680 400">
  <!-- Background shapes -->
  <circle cx="200" cy="200" r="150" fill="#EEEDFE" opacity="0.8"/>
  <circle cx="480" cy="180" r="120" fill="#E1F5EE" opacity="0.8"/>

  <!-- Overlapping geometric forms -->
  <rect x="150" y="100" width="200" height="200" rx="8"
        fill="#CECBF6" opacity="0.6"
        transform="rotate(15 250 200)"/>
  <rect x="320" y="80" width="180" height="180" rx="8"
        fill="#9FE1CB" opacity="0.6"
        transform="rotate(-10 410 170)"/>

  <!-- Detail lines -->
  <line x1="100" y1="300" x2="580" y2="300"
        stroke="#534AB7" stroke-width="0.5" opacity="0.3"/>
  <line x1="100" y1="310" x2="580" y2="310"
        stroke="#534AB7" stroke-width="0.5" opacity="0.2"/>
</svg>

Pattern: Radial Symmetry

<svg width="100%" viewBox="0 0 680 680">
  <g transform="translate(340 340)">
    <!-- Repeat a shape at angular intervals -->
    <g transform="rotate(0)">
      <ellipse cx="0" cy="-120" rx="30" ry="80"
               fill="#FAECE7" stroke="#993C1D" stroke-width="0.5"/>
    </g>
    <g transform="rotate(45)">
      <ellipse cx="0" cy="-120" rx="30" ry="80"
               fill="#FBEAF0" stroke="#993556" stroke-width="0.5"/>
    </g>
    <!-- ... repeat for 90, 135, 180, 225, 270, 315 -->
  </g>
</svg>

Pattern: Landscape with Layered Shapes

For physical scenes, use ALL hardcoded hex (no theme classes):

<svg width="100%" viewBox="0 0 680 400">
  <!-- Sky -->
  <rect x="0" y="0" width="680" height="250" fill="#E6F1FB"/>
  <!-- Mountains -->
  <polygon points="0,250 150,100 300,250" fill="#B4B2A9"/>
  <polygon points="200,250 400,60 600,250" fill="#888780"/>
  <!-- Ground -->
  <rect x="0" y="250" width="680" height="150" fill="#C0DD97"/>
  <!-- Sun -->
  <circle cx="550" cy="80" r="40" fill="#FAC775"/>
</svg>

Part 6: Advanced Patterns

Tabbed / Multi-View Interfaces

Since html streams top-down, don't use display: none during streaming. Instead, render all content stacked, then let a jsExpression create the tabs once the document is complete:

css parameter:

#tabs { display: flex; gap: 4px; margin-bottom: 16px; }

html parameter:

<div id="tabs">
  <button onclick="showTab(0)">Overview</button>
  <button onclick="showTab(1)">Details</button>
  <button onclick="showTab(2)">Code</button>
</div>

<div id="panel-0"><!-- Overview content --></div>
<div id="panel-1"><!-- Details content --></div>
<div id="panel-2"><!-- Code content --></div>

jsFunctions parameter:

function showTab(n) {
  for (let i = 0; i < 3; i++) {
    document.getElementById('panel-' + i).style.display =
      i === n ? 'block' : 'none';
  }
  document.querySelectorAll('#tabs button').forEach((b, i) => {
    b.style.fontWeight = i === n ? '500' : '400';
    b.style.color = i === n
      ? 'var(--color-text-primary)' : 'var(--color-text-tertiary)';
  });
}

jsExpressions parameter:

showTab(0);

sendPrompt — Chat-Driven Interactivity

The host bridge exposes await Websandbox.connection.remote.sendPrompt({ text }), which sends a message as if the user typed it. Use it when the user's next action benefits from AI thinking. Wire it through a named jsFunction:

jsFunctions parameter:

function drillDown(text) {
  Websandbox.connection.remote.sendPrompt({ text });
}

html parameter:

<button onclick="drillDown('Break down Q4 revenue by region')">
  Drill into Q4 ↗
</button>
<button onclick="drillDown('Explain what shear force is')">
  Learn about shear ↗
</button>

Use for: drill-downs, follow-up questions, "explain this part". Don't use for: filtering, sorting, toggling — handle those in JS. Append to button text when it triggers the bridge. For external links use await Websandbox.connection.remote.openLink({ url }) (https only — anything else is rejected).

Responsive Grid Pattern

display: grid;
grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
gap: 12px;

Use minmax(0, 1fr) if children have large min-content that could overflow.

CSS Animations (Subtle and Purposeful)

/* Only animate transform and opacity for performance */
@keyframes fadeSlideIn {
  from { opacity: 0; transform: translateY(8px); }
  to { opacity: 1; transform: translateY(0); }
}

/* Always respect user preferences */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
  }
}

/* Flowing particles / convection currents */
@keyframes flow { to { stroke-dashoffset: -20; } }
.flowing {
  stroke-dasharray: 5 5;
  animation: flow 1.6s linear infinite;
}

/* Pulsing for active elements */
@keyframes pulse {
  0%, 100% { opacity: 0.3; }
  50% { opacity: 0.7; }
}

Part 7: External Libraries

Importmap Libraries (Pre-Injected)

An importmap for three, gsap, d3, and chart.js (served via esm.sh) is pre-injected into every sandbox. jsFunctions and jsExpressions execute as classic scripts, where top-level await is a SyntaxError that fails silently — so PREFER loading libraries with dynamic imports INSIDE an async function declared in jsFunctions, and keep jsExpressions synchronous invocations of those functions:

async function setupScene() {
  const THREE = await import('three');
  const { OrbitControls } =
    await import('three/examples/jsm/controls/OrbitControls.js');
  // ... build the scene with real geometry and PBR materials.
  // NEVER fake 3D with CSS transforms or Canvas 2D projection.
}
async function setupLibraries() {
  const { default: gsap } = await import('gsap');
  const d3 = await import('d3');
  const { default: Chart } = await import('chart.js/auto');
  // ... use the libraries here.
}

Where a module script genuinely belongs in the html parameter, <script type="module"> with bare specifiers also resolves through the importmap:

<script type="module">
import * as THREE from 'three';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
// ... your Three.js code here
</script>

Critical: Regular <script> tags cannot use import statements — use <script type="module"> in html. In jsFunctions/jsExpressions (classic-script semantics) dynamic await import(...) works only inside an async function body, never at top level.

CDN Allowlist (For Everything Else)

Only these CDN origins work (CSP-enforced):

  • cdnjs.cloudflare.com
  • esm.sh
  • cdn.jsdelivr.net
  • unpkg.com

<script src> / <link> CDN tags still work in the html head for libraries outside the importmap:

Mermaid (ERDs, sequence diagrams, class diagrams):

<script type="module">
import mermaid from 'https://esm.sh/mermaid@11/dist/mermaid.esm.min.mjs';
</script>

Tone.js (audio synthesis):

<script src="https://cdnjs.cloudflare.com/ajax/libs/tone/14.8.49/Tone.min.js"></script>

Three.js Coordinate Conventions

Three.js uses a right-handed Y-up coordinate system:

  • X = right (positive) / left (negative)
  • Y = up (positive) / down (negative)
  • Z = toward the viewer (positive) / away from the viewer (negative)

Critical for vehicles and aircraft: The fuselage/body extends along Z (nose at -Z, tail at +Z). Wings extend along X (left/right). The vertical stabilizer extends along Y.

When building an aircraft from primitives:

  • Fuselage = cylinder or box, long axis along Z (use geometry default or rotate 90° around X)
  • Wings = flat box, wide along X, thin along Y, short along Z
  • Tail fin = flat box, tall along Y, thin along X, short along Z
// Correct aircraft orientation example:
// Fuselage along Z
const fuselage = new THREE.Mesh(
  new THREE.CylinderGeometry(0.15, 0.08, 2.0, 12),
  material
);
fuselage.rotation.x = Math.PI / 2; // CylinderGeometry default is Y-up, rotate to Z-forward

// Wings along X
const wing = new THREE.Mesh(
  new THREE.BoxGeometry(2.5, 0.03, 0.4), // wide X, thin Y, short Z
  material
);

// Vertical stabilizer along Y
const tailFin = new THREE.Mesh(
  new THREE.BoxGeometry(0.03, 0.4, 0.3), // thin X, tall Y, short Z
  material
);
tailFin.position.set(0, 0.2, 0.9); // above and behind

Rotation axes for flight dynamics:

  • Pitch = rotation around X (nose up/down)
  • Roll = rotation around Z (wings tilt)
  • Yaw = rotation around Y (nose left/right)

Common mistake: Using the wing box as the fuselage (wide along X instead of Z). Always verify: the longest dimension of the fuselage should be along Z.


Part 8: Quality Checklist

Before producing any visual, run through this:

Functional

  • Does it work without JavaScript during streaming? (Content visible)
  • All styles live in the css parameter (no <style> blocks in html)
  • Behavior is split into jsFunctions (parameterized toolbox) + jsExpressions (one invocation per statement)
  • Do all interactive controls have event handlers?
  • Are all displayed numbers rounded properly?
  • Does the canvas/SVG fit within the container width?

Visual

  • Dark mode test: would every element be readable on near-black?
  • No hardcoded text colors in HTML (use CSS variables)
  • No gradients, shadows, blur, or glow
  • Borders are 0.5px (except 2px for featured item accent)
  • Font weights are only 400 or 500
  • All text is sentence case

Content

  • Explanatory text is in the response, not inside the widget
  • No titles or headings embedded in the HTML output
  • Visual is self-explanatory without reading the narration
  • Narration adds value beyond what the visual shows
  • Offered a clear "go deeper" path

Accessibility

  • @media (prefers-reduced-motion: reduce) for all animations
  • Text contrast is sufficient (dark text on light fills, vice versa)
  • Interactive elements are large enough to click (min 44px touch target)
  • No information conveyed by color alone

Part 9: Decision Matrix — Picking the Right Visual

User asks about...Output typeTechnology
How X works (physical)Illustrative diagramSVG
How X works (abstract)Interactive explainerHTML + inline SVG
Process / stepsFlowchartSVG
Architecture / containmentStructural diagramSVG
Database schema / ERDRelationship diagramMermaid
Trends over timeLine chartChart.js
Category comparisonBar chartChart.js
Part of wholeDoughnut chartChart.js
KPIs / metricsDashboardHTML metric cards
Design a UIMockupHTML
Choose between optionsComparison cardsHTML grid
Cyclic processStep-throughHTML stepper
Physics / mathSimulationCanvas + JS
Function / equationPlotterSVG + JS
Data explorationSortable tableHTML + JS
Creative / decorativeArt / illustrationSVG
3D visualization3D sceneThree.js
Music / audioSynthesizerTone.js
Network / graphForce layoutD3.js
Quick factual answerPlain textNone
Code solutionCode blockNone
Emotional supportWarm textNone

© 2026 YourAI.tools. Every skill from an identity-verified publisher.

Independent catalog. Not affiliated with, endorsed by, or sponsored by Anthropic or any listed publisher. All trademarks belong to their respective owners.