Skip to documentation
KiCad WebViewDocumentation · 0.1.0-beta.1
Download release ↓

Examples

Release 0.1.0-beta.1 · API 1.0-beta · Source-available license 1.0

These independent examples use the 0.1.0-beta.1 ES module directly from the versioned release. Paste one example into its own HTML page served over HTTPS or localhost. No package manager or build step is needed. The public inverter fixture lacks external component models, so 3D may show labelled stand-ins.

For a first classic-script embed, ZIP self-host or iframe, use Quick start. All commercial use requires permission under the release’s license 1.0.

Both viewers load the same exact saved PCB. Selecting a supported part or object in one view forwards its saved identity to the other; cameras remain independently navigable. The host owns both viewers and the link group.

<style>
  #views { display:grid; grid-template-columns:repeat(auto-fit,minmax(min(100%,360px),1fr)); gap:12px; }
  #views > div { height:480px; min-width:0; }
</style>
<div id="views"><div id="layout"></div><div id="assembly"></div></div>
<p id="status" role="status">Loading the public inverter…</p>
<script type="module">
import { Viewer, linkViewers } from 'https://wayri.github.io/assets/lib/kicad-webview/releases/0.1.0-beta.1/kivi-viewer.js';
const project = 'https://wayri.github.io/assets/lib/kicad-webview/releases/0.1.0-beta.1/demo/project.json';
const layout = new Viewer('#layout', { theme:'dark' });
const assembly = new Viewer('#assembly', { theme:'dark' });
const abort = new AbortController();
let links;
window.addEventListener('pagehide', () => {
  abort.abort(); links?.dispose(); layout.dispose(); assembly.dispose();
}, { once:true });
try {
  const loaded = await Promise.all([
    layout.load(project, { file:'3PD+IB.kicad_pcb', view:'pcb', signal:abort.signal }),
    assembly.load(project, { file:'3PD+IB.kicad_pcb', view:'3d', signal:abort.signal })
  ]);
  if (loaded.every(Boolean)) {
    links = linkViewers([layout, assembly]);
    document.querySelector('#status').textContent = 'Select a saved component or copper object.';
  }
} catch (error) {
  if (!abort.signal.aborted) document.querySelector('#status').textContent = error.message;
}
</script>

For schematic cross-probing, load the actual sheet containing the saved symbol in another viewer. Equal reference or net text alone is not a match. See project and link APIs.

Add an opt-in board tour

This example shows the first slide explicitly. The carousel supplies its own Previous/Next/Play controls. Presentation rendering is also an explicit opt-in and may fall back to Balanced.

<div id="board" style="height:70dvh;min-height:360px"></div>
<div id="tour"></div>
<button id="quality" type="button">Try presentation rendering</button>
<p id="status" role="status"></p>
<script type="module">
import { Viewer, ViewerCarousel } from 'https://wayri.github.io/assets/lib/kicad-webview/releases/0.1.0-beta.1/kivi-viewer.js';
const viewer = new Viewer('#board', { theme:'dark' });
const button = document.querySelector('#quality'); button.disabled = true;
let tour;
window.addEventListener('pagehide', () => { tour?.dispose(); viewer.dispose(); }, { once:true });
try {
  if (await viewer.load('https://wayri.github.io/assets/lib/kicad-webview/releases/0.1.0-beta.1/demo/project.json', { view:'3d' })) {
    tour = new ViewerCarousel('#tour', { viewer, interval:6500, slides:[
      { label:'Assembly overview', view:'3d', camera3D:'isometric', clearSelection:true },
      { label:'Top assembly', view:'3d', camera3D:'top' },
      { label:'Bottom assembly', view:'3d', camera3D:'bottom' }
    ] });
    tour.addEventListener('error', e => { document.querySelector('#status').textContent = e.detail.error.message; });
    await tour.show(0); button.disabled = false;
  }
} catch (error) { document.querySelector('#status').textContent = error.message; }
button.addEventListener('click', () => {
  document.querySelector('#status').textContent = 'Active quality: ' + viewer.setRenderQuality('presentation');
});
</script>

Avoid concurrent direct loads while a slide transition is in progress. Dispose the carousel before its viewer. This renders saved geometry; missing models and their warnings remain. See the carousel reference and viewer rendering methods.

Show a sheet hierarchy

The tree loads listed schematic sources independently of a PCB/WebGL viewer. SVG export contains the bounded diagram, not the native schematic source or an electrical netlist.

<div id="tree" style="height:60dvh;min-height:360px"></div>
<button id="expand" type="button" disabled>Expand all</button>
<button id="save" type="button" disabled>Download diagram SVG</button>
<p id="status" role="status"></p>
<script type="module">
import { ProjectTree } from 'https://wayri.github.io/assets/lib/kicad-webview/releases/0.1.0-beta.1/kivi-viewer.js';
const tree = new ProjectTree('#tree', { theme:'dark' });
const expand = document.querySelector('#expand'), save = document.querySelector('#save');
window.addEventListener('pagehide', () => tree.dispose(), { once:true });
try {
  if (await tree.load('https://wayri.github.io/assets/lib/kicad-webview/releases/0.1.0-beta.1/demo/project.json')) {
    expand.disabled = save.disabled = false;
  }
} catch (error) { document.querySelector('#status').textContent = error.message; }
expand.addEventListener('click', () => tree.expandAll());
save.addEventListener('click', () => {
  const url = URL.createObjectURL(new Blob([tree.toSVG()], { type:'image/svg+xml' }));
  const link = document.createElement('a'); link.href = url; link.download = 'inverter-hierarchy.svg';
  document.body.append(link); link.click(); link.remove();
  setTimeout(() => URL.revokeObjectURL(url), 1000);
});
</script>

Use a complete manifest or file bundle to resolve child sheets. A direct root schematic URL can show its child references as missing. See hierarchy reference.

Paint an interactive virtual display

Choose an actual planar component face after the project loads. This creates a virtual graphic, not firmware or an electrical display. Tap it to update a touch marker. The display does not navigate to arbitrary URLs supplied by the design.

<div id="board" style="height:70dvh;min-height:400px"></div>
<button id="choose" type="button" disabled>Choose a display face</button>
<p id="status" role="status">Loading…</p>
<script type="module">
import { Viewer, DemoController } from 'https://wayri.github.io/assets/lib/kicad-webview/releases/0.1.0-beta.1/kivi-viewer.js';
const viewer = new Viewer('#board', { theme:'dark', inspectOnSelect:false });
const choose = document.querySelector('#choose'), status = document.querySelector('#status');
let demo, taps = 0, marker;
window.addEventListener('pagehide', () => { demo?.dispose(); viewer.dispose(); }, { once:true });
function paint() {
  demo.drawDisplay('screen', (ctx, canvas) => {
    ctx.fillStyle = '#091117'; ctx.fillRect(0, 0, canvas.width, canvas.height);
    ctx.fillStyle = '#dfbf48'; ctx.font = 'bold 48px sans-serif';
    ctx.fillText('VIRTUAL DISPLAY', 40, 80);
    ctx.fillStyle = '#edf2f4'; ctx.font = '36px sans-serif';
    ctx.fillText('Tap count: ' + taps, 40, 145);
    if (marker) {
      ctx.beginPath(); ctx.arc(marker.x * canvas.width, (1-marker.y) * canvas.height, 18, 0, Math.PI*2); ctx.fill();
    }
  });
}
try {
  if (await viewer.load('https://wayri.github.io/assets/lib/kicad-webview/releases/0.1.0-beta.1/demo/project.json', { view:'3d' })) {
    choose.disabled = false; status.textContent = 'Choose a face on the loaded 3D geometry.';
  }
} catch (error) { status.textContent = error.message; }
choose.addEventListener('click', async () => {
  choose.disabled = true;
  try {
    const face = await viewer.pickDemoFace();
    demo?.dispose(); demo = new DemoController(viewer);
    demo.addDisplay('screen', { face, mode:'tft', width:1024, height:768,
      onRelease: event => {
        if (!event.cancelled && event.uv) { marker = event.uv; taps++; paint(); }
      }
    });
    demo.addEventListener('suspend', () => { status.textContent = 'Scene changed; choose a face again.'; });
    demo.addEventListener('error', event => { status.textContent = event.detail.error.message; });
    paint(); status.textContent = 'Tap the selected face. This is a scripted presentation.';
  } catch (error) { status.textContent = error.message; }
  finally { choose.disabled = false; }
});
</script>

The UV-to-pixel mapping is chosen by this example. A face’s geometry sets the physical aspect ratio; a selected token is invalid after source/view replacement. Buttons, knobs and LEDs use explicit saved anchors or supported faces; check their identities rather than copying references from another design. See DemoController API.

Attach a reviewed analysis file

This example loads the public PCB in 2D and lets the reader choose an analysis JSON file locally. The data must contain the exact board source hash and the release’s validated rectangular-sample schema. It supplies no fabricated numerical result and does not run a solver.

<div id="board" style="height:70dvh;min-height:360px"></div>
<label>Matching analysis JSON <input id="results" type="file" accept=".json" disabled></label>
<p id="status" role="status"></p>
<script type="module">
import { Viewer } from 'https://wayri.github.io/assets/lib/kicad-webview/releases/0.1.0-beta.1/kivi-viewer.js';
const viewer = new Viewer('#board');
const input = document.querySelector('#results'), status = document.querySelector('#status');
window.addEventListener('pagehide', () => viewer.dispose(), { once:true });
try {
  if (await viewer.load('https://wayri.github.io/assets/lib/kicad-webview/releases/0.1.0-beta.1/demo/project.json', { view:'pcb' })) input.disabled = false;
} catch (error) { status.textContent = error.message; }
input.addEventListener('change', async () => {
  const file = input.files[0]; if (!file) return;
  try {
    if (file.size > 8 * 1024 * 1024) throw new Error('Analysis JSON exceeds 8 MiB.');
    if (await viewer.setAnalysis(await file.text())) {
      status.textContent = 'Result loaded. Review its provenance, units and assumptions in the viewer.';
    }
  } catch (error) { status.textContent = error.message; }
});
</script>

The library rejects a wrong source hash rather than fitting the data to a similar-looking PCB. For another board, load its exact saved source and provide matching results. See analysis contract for coordinate convention, fields, provenance and limits.

Integrate with a component framework

Construct a viewer after the mount element exists. Cancel pending work on unmount, dispose helper objects before their viewer, and ignore completions after the component is no longer current. Do not create a new renderer on every reactive value update. Use the viewer’s setters for theme, layers and selection; source replacement has its own cancellation and disposal semantics.

For share-link replay, first load the allowed project, then validate/restore the state while linked propagation is suspended. A restore is not transactional, and it never grants permission to fetch an arbitrary fragment-derived URL. See the viewer lifecycle, view-state API and hosting/security guidance.