From noir
Web integration for Noir circuits. Covers React integration, Web Worker proving, WASM setup, and UX patterns for browser-based ZK applications.
npx claudepluginhub critesjosh/noir-claude-plugin --plugin noirThis skill is limited to using the following tools:
Build browser-based ZK applications with Noir circuits using React, Web Workers, and WASM.
Generates design tokens/docs from CSS/Tailwind/styled-components codebases, audits visual consistency across 10 dimensions, detects AI slop in UI.
Records polished WebM UI demo videos of web apps using Playwright with cursor overlay, natural pacing, and three-phase scripting. Activates for demo, walkthrough, screen recording, or tutorial requests.
Delivers idiomatic Kotlin patterns for null safety, immutability, sealed classes, coroutines, Flows, extensions, DSL builders, and Gradle DSL. Use when writing, reviewing, refactoring, or designing Kotlin code.
Build browser-based ZK applications with Noir circuits using React, Web Workers, and WASM.
React Component (main thread)
|
| postMessage({ type: 'prove', circuit, inputs })
v
Web Worker (background thread)
|
| noir.execute(inputs) --> backend.generateProof(witness)
v
WASM (barretenberg, running inside worker)
|
| postMessage({ type: 'proof-generated', proof })
v
React Component (updates UI with proof)
Proving is CPU-intensive and will freeze the browser if run on the main thread. A medium-sized circuit can block the UI for 5-30 seconds. Large circuits can take minutes.
Never do this:
// BAD: blocks the main thread
const { proof, publicInputs } = await backend.generateProof(witness);
Always delegate to a Web Worker:
// GOOD: runs in background thread
worker.postMessage({ type: 'prove', circuit, inputs });
SharedArrayBuffer is required by barretenberg and needs Cross-Origin-Isolation HTTP headers:
Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Embedder-Policy: require-corp
Without these headers, SharedArrayBuffer is undefined and WASM initialization will fail. See WASM Setup for framework-specific configuration.
// App.tsx
import { useState, useRef, useEffect } from "react";
import circuit from "../target/my_circuit.json";
function ProveButton() {
const [status, setStatus] = useState<"idle" | "proving" | "done" | "error">("idle");
const workerRef = useRef<Worker | null>(null);
useEffect(() => {
const worker = new Worker(new URL("./proof-worker.ts", import.meta.url));
worker.onmessage = (e) => {
if (e.data.type === "proof-generated") setStatus("done");
if (e.data.type === "error") setStatus("error");
};
workerRef.current = worker;
return () => worker.terminate();
}, []);
const prove = () => {
setStatus("proving");
workerRef.current?.postMessage({
type: "prove",
circuit,
inputs: { x: "3", y: "4" },
});
};
return (
<div>
<button onClick={prove} disabled={status === "proving"}>
{status === "proving" ? "Proving..." : "Generate Proof"}
</button>
{status === "done" && <p>Proof generated successfully.</p>}
{status === "error" && <p>Proving failed. Try refreshing the page.</p>}
</div>
);
}