WebAssembly mit Claude Code integrieren: Rust, wasm-pack und Vite
Praxisguide für Rust WebAssembly mit Claude Code, Vite, typisierten Wrappern, Benchmarks und typischen Fallen.
Was Claude Code mit WebAssembly leisten soll
WebAssembly, kurz Wasm, ist ein portables Binärformat, mit dem Code aus Rust, C, C++ und anderen Sprachen im Browser oder in Node.js laufen kann. Es ist kein allgemeiner Ersatz für JavaScript. In echten Projekten ist Wasm am stärksten, wenn es einen klar begrenzten, rechenintensiven Teil übernimmt: Bildverarbeitung, Kompression, Byte-Verarbeitung für Kryptografie, numerische Schleifen, CSV-Aggregation oder die Wiederverwendung bestehender Rust/C++-Logik.
Claude Code hilft, weil eine saubere Wasm-Integration mehrere Schichten umfasst. Es reicht nicht, eine Rust-Funktion zu schreiben. Du brauchst den wasm-pack Build, die von wasm-bindgen erzeugte JavaScript-Brücke, einen asynchronen Einstieg in Vite, einen TypeScript-Wrapper, einen Benchmark und eine Review-Anweisung, die Grenzkosten betrachtet. Die JS-Wasm-Grenze ist der Übergabepunkt zwischen JavaScript und WebAssembly. Wenn Daten dort zu oft hin und her wandern, kann der Geschwindigkeitsgewinn verschwinden.
In diesem Artikel bauen wir eine kleine, kopierbare Grundlage: RGBA-Bilddaten invertieren, eine numerische CSV-Spalte summieren und einen leichten Checksum über Bytes berechnen. Diese Beispiele stehen für Bilddaten, Textdaten und Binärdaten. Das Muster lässt sich später auf Browser-interne Hochgeschwindigkeitsverarbeitung, Portierung vorhandener Rust/C++-Assets, Kompression, eigene Codecs oder lokale Berechnungen mit sensiblen Daten erweitern. Für den größeren Performance-Kontext passt Claude Code performance optimization.
Nutze parallel die offiziellen Quellen: MDN WebAssembly für die Plattform, wasm-bindgen Guide für die Rust-JavaScript-Brücke und das wasm-pack repository für den Build-Workflow. Claude Code sollte diese Grenzen respektieren, statt unnötig einen eigenen Loader zu erfinden.
Den Use Case vor dem Code festlegen
Wasm ist nicht automatisch schneller. Es lohnt sich, wenn ein größerer Datenblock einmal übergeben und dann in engen Schleifen verarbeitet wird. Es lohnt sich selten, wenn JavaScript tausende kleine Wasm-Aufrufe ausführt. Deshalb sollte die erste Frage lauten: Welche Operation rechtfertigt die Grenze, und wie messen wir sie?
| Use Case | Warum Wasm passt | Was Claude Code prüfen soll |
|---|---|---|
| Bildverarbeitung | RGBA-Buffer lassen sich in linearen Schleifen verarbeiten | Kopien, Canvas-Lesezeit und fairer Benchmark |
| Kryptografie, Kompression, Codecs | Byte-Arrays passen gut zu Rust-Bibliotheken | Auditierte Bibliothek oder erlaubter Eigenbau |
| CSV und numerische Berechnung | Parsing und Aggregation wiederholen viele Schritte | Leere Zeilen, NaN, große Dateien und Fehlerstrategie |
| Rust/C++ portieren | Bewährte Logik kann im Browser wiederverwendet werden | OS-APIs, Datei-I/O, Threads und inkompatible Abhängigkeiten |
| Schnelle Browser-Verarbeitung | Daten bleiben lokal auf dem Gerät | Startgröße, Fallback und Zielbrowser |
In Masas Tests war es hilfreicher, eine einzelne Funktion zu portieren und zu messen, statt direkt ein ganzes Feature umzubauen. Bei Bildern war die Rust-Funktion schnell, aber Canvas-Lese- und Schreibzeit konnten dominieren. Bei CSV war ein einmaliger Aufruf mit dem gesamten Text stabiler als ein Aufruf pro Zeile. Diese Einschränkungen gehören in den Prompt für Claude Code.
Minimales Rust-Modul mit wasm-pack
wasm-pack baut das Rust-Crate, ruft wasm-bindgen auf und erzeugt einen pkg Ordner mit Wasm-Binary, JavaScript-Loader, Paketmetadaten und TypeScript-Deklarationen. wasm-bindgen ist die Bibliothek, die ausgewählte Rust-Funktionen für JavaScript sichtbar macht.
# Cargo.toml
[package]
name = "wasm-lab"
version = "0.1.0"
edition = "2021"
[lib]
crate-type = ["cdylib"]
[dependencies]
wasm-bindgen = "0.2"
// src/lib.rs
use wasm_bindgen::prelude::*;
#[wasm_bindgen]
pub fn invert_rgba(pixels: &mut [u8]) {
for chunk in pixels.chunks_exact_mut(4) {
chunk[0] = 255 - chunk[0];
chunk[1] = 255 - chunk[1];
chunk[2] = 255 - chunk[2];
}
}
#[wasm_bindgen]
pub fn sum_csv_column(csv: &str, column: usize) -> f64 {
csv.lines()
.filter(|line| !line.trim().is_empty())
.filter_map(|line| line.split(',').nth(column))
.filter_map(|cell| cell.trim().parse::<f64>().ok())
.sum()
}
#[wasm_bindgen]
pub fn fnv1a32(bytes: &[u8]) -> u32 {
let mut hash = 0x811c9dc5u32;
for byte in bytes {
hash ^= u32::from(*byte);
hash = hash.wrapping_mul(0x01000193);
}
hash
}
rustup target add wasm32-unknown-unknown
cargo install wasm-pack
wasm-pack build --target web --out-dir pkg
fnv1a32 ist hier kein sicherer kryptografischer Hash. Für Passwörter, Signaturen, Zahlungen oder Tokens solltest du Web Crypto API oder eine auditierte Bibliothek verwenden. In diesem Guide dient die Funktion nur als kleines Beispiel für Byte-Daten.
In Vite laden und typisiert kapseln
Nach dem Build entstehen pkg/wasm_lab.js und pkg/wasm_lab.d.ts. In Vite importierst du das generierte Modul, wartest auf init() und kapselst den Zugriff in einem Wrapper. So ruft die UI keine Wasm-Funktion zu früh auf und initialisiert das Modul nicht bei jeder Nutzeraktion neu.
// src/wasm-client.ts
import init, {
fnv1a32,
invert_rgba,
sum_csv_column,
} from "../pkg/wasm_lab";
export type WasmClient = {
invertImage(imageData: ImageData): Promise<ImageData>;
sumCsvColumn(csv: string, columnIndex: number): Promise<number>;
checksum(bytes: Uint8Array): Promise<number>;
};
let initPromise: Promise<void> | undefined;
async function ensureWasm(): Promise<void> {
initPromise ??= init().then(() => undefined);
return initPromise;
}
export const wasmClient: WasmClient = {
async invertImage(imageData) {
await ensureWasm();
const pixels = new Uint8Array(
imageData.data.buffer,
imageData.data.byteOffset,
imageData.data.byteLength,
);
invert_rgba(pixels);
return imageData;
},
async sumCsvColumn(csv, columnIndex) {
await ensureWasm();
return sum_csv_column(csv, columnIndex);
},
async checksum(bytes) {
await ensureWasm();
return fnv1a32(bytes);
},
};
// src/main.ts
import { wasmClient } from "./wasm-client";
const fileInput = document.querySelector<HTMLInputElement>("#csv-file");
const output = document.querySelector<HTMLPreElement>("#output");
fileInput?.addEventListener("change", async () => {
const file = fileInput.files?.[0];
if (!file || !output) return;
const csv = await file.text();
const total = await wasmClient.sumCsvColumn(csv, 2);
output.textContent = `column 2 total: ${total.toFixed(2)}`;
});
Für diesen wasm-pack --target web Ablauf reicht anfangs die Standardkonfiguration von Vite. Plugins werden erst interessant, wenn du rohe .wasm Dateien direkt importierst oder einen anderen Bundling-Ansatz mit top-level await nutzt. Die meisten Startfehler liegen bei Pfaden und Initialisierung.
Review-Prompt für Claude Code
Claude Code sollte nach der Implementierung kritisch prüfen. Der Review-Prompt muss enger sein als der Implementierungs-Prompt und konkrete Punkte nennen: asynchrone Initialisierung, Kopien, DOM-Grenze, Typen, Bundle-Größe und Benchmark.
Review only these files:
- src/lib.rs
- pkg/wasm_lab.d.ts
- src/wasm-client.ts
- src/main.ts
- src/bench.ts
Goal:
Integrate the Rust WebAssembly module into the Vite app without changing UI behavior.
Check:
1. init() is awaited before any exported Wasm function is called.
2. init() is cached and not repeated for every click or file upload.
3. Large arrays cross the JS-Wasm boundary at most once per user action.
4. DOM updates stay in TypeScript, not inside Rust.
5. The wrapper exposes typed methods and keeps generated pkg files out of hand edits.
6. Benchmarks compare the same input data for JavaScript and Wasm.
Run:
wasm-pack build --target web --out-dir pkg
npm run typecheck
npm run build
In Teams gehören diese Regeln in CLAUDE.md. Dann bekommt jede Wasm-Änderung dieselbe Prüfung, unabhängig davon, wer gerade den Prompt schreibt.
Benchmark und Verifikation
Eine Wasm-Migration sollte nicht wegen eines Gefühls gemergt werden. Miss denselben Input und denselben Output. Der folgende Benchmark vergleicht RGBA-Invertierung in JavaScript und Wasm.
// src/bench.ts
import { wasmClient } from "./wasm-client";
function invertJs(pixels: Uint8Array): void {
for (let index = 0; index < pixels.length; index += 4) {
pixels[index] = 255 - pixels[index];
pixels[index + 1] = 255 - pixels[index + 1];
pixels[index + 2] = 255 - pixels[index + 2];
}
}
function cloneImageData(source: Uint8Array, width: number, height: number): ImageData {
return new ImageData(new Uint8ClampedArray(source), width, height);
}
export async function runBench(): Promise<void> {
const width = 1920;
const height = 1080;
const source = new Uint8Array(width * height * 4);
crypto.getRandomValues(source);
const jsPixels = new Uint8Array(source);
const wasmImage = cloneImageData(source, width, height);
const jsStart = performance.now();
invertJs(jsPixels);
const jsMs = performance.now() - jsStart;
const wasmStart = performance.now();
await wasmClient.invertImage(wasmImage);
const wasmMs = performance.now() - wasmStart;
console.table({
javascriptMs: Number(jsMs.toFixed(2)),
wasmMs: Number(wasmMs.toFixed(2)),
ratio: Number((jsMs / wasmMs).toFixed(2)),
});
}
wasm-pack build --target web --out-dir pkg
npm run typecheck
npm run build
npm run dev
Wenn Wasm enttäuscht, prüfe zuerst Datenumwandlungen. Canvas, ImageData, Strings und Development-Builds können den eigentlichen Rechengewinn verdecken. Gib Claude Code die Messergebnisse und frage, ob Wasm bleiben soll, ein Web Worker besser ist oder JavaScript-Optimierung reicht.
Typische Fallen
Erste Falle: Die Initialisierung ist asynchron. init() muss abgeschlossen sein, bevor Exports aufgerufen werden. Cache die Promise im Wrapper.
Zweite Falle: Bundle-Größe. Jede Rust-Abhängigkeit kann das .wasm vergrößern. Starte mit einer Funktion und prüfe das Production-Build.
Dritte Falle: JS-Wasm-Grenzkosten. Vermeide viele kleine Aufrufe und übergib größere Arrays, Strings oder Buffer.
Vierte Falle: DOM aus Wasm steuern. Events, Rendering, Barrierefreiheit und Fehlermeldungen bleiben in TypeScript; Rust macht reine Berechnung.
Fünfte Falle: Versteckte Speicherkopien. Typed Arrays, Strings und ImageData können in den Bindings kopiert werden. Der Benchmark muss diese Kosten enthalten.
Sechste Falle: Browser-Kompatibilität und Sicherheitsheader. Basis-Wasm ist breit unterstützt, aber Wasm threads und SharedArrayBuffer brauchen COOP und COEP. Mit Werbung, iframes oder CDN solltest du früh testen.
Team-Einführung und CTA
Für einen Einzeltest reicht dieser Code. Im Team musst du festlegen, welche Logik nach Rust geht, was in TypeScript bleibt, wie generierte pkg Dateien behandelt werden, welche Browser unterstützt werden und welcher Benchmark einen Merge blockiert. Diese Regeln gehören in CLAUDE.md und in die Review-Prompts.
ClaudeCodeLab kann daraus einen Workflow für dein Repository machen: passenden Wasm-Use-Case wählen, Rust/C++-Assets prüfen, Benchmarks entwerfen und das Team im Umgang mit Claude Code schulen. Wenn WebAssembly Produktionsperformance, Datenschutz im Browser oder gemeinsame Frontend-Architektur betrifft, starte mit Claude Code training and consultation.
Verifikationsnotiz
Beim Testen war nicht die Rust-Funktion schwierig, sondern der Zeitpunkt von init(). Sobald die Initialisierung in wasm-client.ts gekapselt war, liefen Bildverarbeitung, CSV-Aggregation und Checksum über denselben Pfad. Kleine Inputs waren in JavaScript schnell genug; bei Full-HD-Buffern und größeren CSVs wurde der Unterschied sichtbarer. Miss deshalb die ganze Grenze, nicht nur den Funktionskörper.
Kostenloses PDF: Claude-Code-Cheatsheet
E-Mail eintragen und eine Seite mit Befehlen, Review-Gewohnheiten und sicheren Workflows herunterladen.
Wir schützen Ihre Daten und senden keinen Spam.
Über den Autor
Masa
Engineer für praktische Claude-Code-Workflows und Team-Einführung.
Ähnliche Artikel
Claude-Code-Permission-Receipt: Scope, Beweis und Rollback festhalten
Permission-Receipt für Claude Code: erlaubte Aktionen, Freigabegrenzen, Prüfbefehle, Rollback und Umsatz-CTA-Prüfung.
Sicheres Agent Harness fur Claude Code und Codex: Rechte, Prufung und Rollback
Ein praktisches Agent Harness fur Claude Code und Codex mit Policy, Plan, Verifikation und Recovery.
Claude Code Subagents: Praxisleitfaden für sichere Agent-Delegation
Claude Code Subagents praktisch nutzen: Artikel- und Codearbeit sicher aufteilen, Prompts einsetzen, Fehler vermeiden.