Agents should emit UI intent, not frontend code.

LensUI renders compact lightcode into polished, live browser UI. Agents describe the surface; the runtime owns layout, charts, media, animation, source updates, and rollback.

What it is.

Small payloads

Agents send semantic nodes and compact rows instead of DOM, JSX, CSS, chart code, and responsive rules.

Renderer-owned polish

The runtime owns visual rhythm, light/dark modes, adaptive containers, stage scroll fallback, embed auto-sizing, media framing, animation, and safe rollback.

Live after render

`setSource` updates fields, lists, charts, progress, and status rows without regenerating the whole UI.

Host-owned authority

Provider keys, auth, billing, memory, files, browser tools, and external effects stay in the host app or gateway.

Why it matters.

62%average savings vs equivalent React component payloads
31%average savings vs direct HTML payloads
1 linepatches can update a metric or chart without resending the stage
0 keysprovider credentials and side effects are outside the renderer

React

147 tokens
function MarketPulse({ data }) {
  return (
    <section className="stage rounded-xl border border-white/15 bg-black p-6 text-white">
      <header className="mb-5">
        <h1 className="text-4xl font-semibold tracking-tight">Market Pulse</h1>
        <p className="text-sm uppercase text-white/55">Live source</p>
      </header>
      <div className="grid grid-cols-3 gap-3">
        <Metric label="BTC" value={data.btc} detail="usd" />
        <Metric label="ETH" value={data.eth} detail="usd" />
        <LineChart data={data.trend} x="t" y="v" />
      </div>
    </section>
  );
}

HTML

110 tokens
<section class="stage">
  <header>
    <h1>Market Pulse</h1>
    <p>Live source</p>
  </header>
  <div class="grid grid-cols-3 gap-3">
    <article class="metric"><span>BTC</span><strong data-bind="markets.btc"></strong><em>usd</em></article>
    <article class="metric"><span>ETH</span><strong data-bind="markets.eth"></strong><em>usd</em></article>
    <canvas data-chart="line" data-source="markets.trend"></canvas>
  </div>
</section>

Lightcode

68 tokens
0DS|markets|https://api.coingecko.com/api/v3/simple/price?ids=bitcoin,ethereum&vs_currencies=usd|ttl=30|mode=poll
0F|st=mono
0V|Market Pulse|Live source
1G|auto|min=180|max=3
2M|BTC|$markets.bitcoin.usd|usd
2M|ETH|$markets.ethereum.usd|usd
1H|line|4,7,6,10,9,13,16|x=t|y=v
54% saved vs React38% saved vs HTML

Start with the runtime.

Install the single `@ardabot/lensui` npm package, mount a stage region, and render lightcode. The package includes the runtime, CLI bridge, and reusable agent skill.

npm install @ardabot/lensui
import { createStageRuntime } from "@ardabot/lensui/html";

const root = document.querySelector("#lens-stage-mount");
const stage = createStageRuntime(root);

stage.setSource("markets", { bitcoin: { usd: 76448 }, ethereum: { usd: 2098 } });

stage.render(`0DS|markets|https://api.coingecko.com/api/v3/simple/price?ids=bitcoin,ethereum&vs_currencies=usd|ttl=30|mode=poll
0F|st=mono|mode=dark
0V|Market Pulse|Live source
1G|auto|min=180|max=3
2M|BTC|$markets.bitcoin.usd|usd
2M|ETH|$markets.ethereum.usd|usd
2M|Tokens|-64%|vs React
1H|line|4,7,6,10,9,13,16|trend|h=190`);

The agent contract.

Render semantic intent

Use nodes like `V`, `G`, `M`, `H`, `MO`, `TL`, `CP`, `ST`, `X`, and `WV`; avoid HTML/React/CSS in ordinary turns.

Patch small edits

For revisions, read the current lightcode and patch the changed lines instead of regenerating a whole stage.

Use live sources

Declare `0DS` sources and `$source.path` bindings; hosts can push updated snapshots into the existing UI.

Size by host

Use `stage` sizing for fullscreen canvases with scroll on overflow, or `auto` sizing for embeds that grow around rendered content.