npx claudepluginhub arustydev/agents --plugin browser-extension-devWant just this skill?
Then install: npx claudepluginhub u/[userId]/[slug]
Guide for integrating WebAssembly modules into browser extensions using wasm-pack, wasm-bindgen, and cross-browser loading patterns.
This skill uses the workspace's default tool permissions.
WASM Extension Integration
Guide for integrating WebAssembly modules into browser extensions using wasm-pack, wasm-bindgen, and cross-browser loading patterns.
Overview
WASM in browser extensions enables:
- High-performance computations (cryptography, parsing, compression)
- Code reuse from Rust/C++ libraries
- Sandboxed execution environments
Build Pipeline
wasm-pack Workflow
# Install wasm-pack
curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh
# Build for web target
wasm-pack build --target web --out-dir pkg
# Build for bundler (if using webpack/vite)
wasm-pack build --target bundler --out-dir pkg
Cargo.toml Configuration
[package]
name = "extension-wasm"
version = "0.1.0"
edition = "2021"
[lib]
crate-type = ["cdylib", "rlib"]
[dependencies]
wasm-bindgen = "0.2"
js-sys = "0.3"
web-sys = { version = "0.3", features = ["console"] }
[profile.release]
opt-level = "s" # Optimize for size
lto = true # Link-time optimization
codegen-units = 1 # Single codegen unit for better optimization
strip = true # Strip debug symbols
Build Targets
| Target | Output | Use Case |
|---|---|---|
web | ES modules | Direct browser loading |
bundler | npm package | Webpack/Vite bundling |
nodejs | CommonJS | Node.js (not for extensions) |
no-modules | Global | Legacy browser support |
WXT Integration
Project Structure
extension/
├── wasm/
│ ├── Cargo.toml
│ └── src/
│ └── lib.rs
├── entrypoints/
│ └── background.ts
└── wxt.config.ts
WXT Configuration
// wxt.config.ts
import { defineConfig } from 'wxt';
export default defineConfig({
vite: () => ({
plugins: [
// WASM plugin for Vite
{
name: 'wasm-pack',
buildStart: async () => {
const { execSync } = await import('child_process');
execSync('wasm-pack build wasm --target web --out-dir ../public/wasm', {
stdio: 'inherit'
});
}
}
],
build: {
target: 'esnext',
rollupOptions: {
output: {
// Preserve WASM imports
inlineDynamicImports: false
}
}
},
optimizeDeps: {
exclude: ['*.wasm']
}
})
});
Cross-Browser Loading
Synchronous Instantiation (Small Modules)
// For modules < 4KB
async function loadWasmSync(wasmPath: string): Promise<WebAssembly.Instance> {
const response = await fetch(chrome.runtime.getURL(wasmPath));
const bytes = await response.arrayBuffer();
const module = new WebAssembly.Module(bytes);
return new WebAssembly.Instance(module);
}
Asynchronous Instantiation (Large Modules)
// For modules >= 4KB (required by browsers)
async function loadWasmAsync(wasmPath: string): Promise<WebAssembly.Instance> {
const response = await fetch(chrome.runtime.getURL(wasmPath));
if (WebAssembly.instantiateStreaming) {
// Chrome, Firefox, Edge - streaming compilation
const { instance } = await WebAssembly.instantiateStreaming(response);
return instance;
} else {
// Safari fallback
const bytes = await response.arrayBuffer();
const { instance } = await WebAssembly.instantiate(bytes);
return instance;
}
}
wasm-bindgen Initialization
// Generated by wasm-pack
import init, { process_data } from './pkg/extension_wasm.js';
let wasmReady = false;
async function initWasm(): Promise<void> {
if (wasmReady) return;
const wasmUrl = chrome.runtime.getURL('pkg/extension_wasm_bg.wasm');
await init(wasmUrl);
wasmReady = true;
}
// Usage
async function processWithWasm(data: Uint8Array): Promise<Uint8Array> {
await initWasm();
return process_data(data);
}
Browser Compatibility
Feature Detection
function checkWasmSupport(): {
basic: boolean;
streaming: boolean;
threads: boolean;
simd: boolean;
} {
const basic = typeof WebAssembly !== 'undefined';
const streaming = basic &&
typeof WebAssembly.instantiateStreaming === 'function';
// Check for threads (SharedArrayBuffer)
const threads = basic &&
typeof SharedArrayBuffer !== 'undefined';
// Check for SIMD
const simd = basic && (() => {
try {
// Minimal SIMD validation bytes
const bytes = new Uint8Array([
0, 97, 115, 109, 1, 0, 0, 0, 1, 5, 1, 96, 0, 1, 123,
3, 2, 1, 0, 10, 10, 1, 8, 0, 65, 0, 253, 15, 253, 98, 11
]);
return WebAssembly.validate(bytes);
} catch {
return false;
}
})();
return { basic, streaming, threads, simd };
}
Safari Considerations
Safari has stricter WASM policies:
// Safari requires explicit WASM MIME type
// Ensure server sends: Content-Type: application/wasm
// Safari doesn't support streaming compilation from cross-origin
// Keep WASM files in extension package
// Safari may require web_accessible_resources for WASM
{
"web_accessible_resources": [
{
"resources": ["pkg/*.wasm", "pkg/*.js"],
"matches": ["<all_urls>"]
}
]
}
Memory Management
Extension Memory Limits
| Context | Chrome | Firefox | Safari |
|---|---|---|---|
| Service worker | 128MB default | 512MB | 128MB |
| Content script | Tab memory | Tab memory | Tab memory |
| Popup | 128MB | 128MB | 128MB |
Memory-Efficient Patterns
// lib.rs - Minimize allocations
use wasm_bindgen::prelude::*;
// Reuse allocations
static mut BUFFER: Vec<u8> = Vec::new();
#[wasm_bindgen]
pub fn process_chunk(data: &[u8]) -> Vec<u8> {
unsafe {
BUFFER.clear();
BUFFER.extend_from_slice(data);
// Process in place
BUFFER.clone()
}
}
// Free memory explicitly
#[wasm_bindgen]
pub fn free_buffer() {
unsafe {
BUFFER = Vec::new();
BUFFER.shrink_to_fit();
}
}
Streaming Processing
// Process large data in chunks to avoid memory spikes
async function processLargeData(
data: ArrayBuffer,
chunkSize: number = 1024 * 1024 // 1MB chunks
): Promise<ArrayBuffer> {
await initWasm();
const input = new Uint8Array(data);
const results: Uint8Array[] = [];
for (let i = 0; i < input.length; i += chunkSize) {
const chunk = input.slice(i, i + chunkSize);
const processed = process_chunk(chunk);
results.push(processed);
}
// Combine results
const totalLength = results.reduce((sum, arr) => sum + arr.length, 0);
const output = new Uint8Array(totalLength);
let offset = 0;
for (const result of results) {
output.set(result, offset);
offset += result.length;
}
// Free WASM memory
free_buffer();
return output.buffer;
}
Service Worker Integration
Loading WASM in Service Worker
// background.ts (service worker)
let wasmModule: WebAssembly.Module | null = null;
// Pre-compile on install
chrome.runtime.onInstalled.addListener(async () => {
const response = await fetch(chrome.runtime.getURL('pkg/module.wasm'));
const bytes = await response.arrayBuffer();
wasmModule = await WebAssembly.compile(bytes);
// Store compiled module reference for quick instantiation
console.log('WASM module compiled');
});
// Instantiate when needed
async function getWasmInstance(): Promise<WebAssembly.Instance> {
if (!wasmModule) {
const response = await fetch(chrome.runtime.getURL('pkg/module.wasm'));
const bytes = await response.arrayBuffer();
wasmModule = await WebAssembly.compile(bytes);
}
return new WebAssembly.Instance(wasmModule);
}
Service Worker Lifecycle
// Handle service worker termination
// WASM modules are lost when worker sleeps
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
if (message.type === 'PROCESS_WASM') {
// Re-initialize WASM if needed
processWithWasm(message.data)
.then(sendResponse)
.catch(error => sendResponse({ error: error.message }));
return true;
}
});
MCP Server: wasm-bindgen
The plugin includes a wasm-bindgen MCP server for build automation.
Configuration
{
"mcpServers": {
"wasm-bindgen": {
"command": "cargo",
"args": [
"run",
"--manifest-path",
"${HOME}/.local/share/mcp/wasm-bindgen-mcp/Cargo.toml",
"--",
"--target-dir",
".output/wasm"
],
"env": {
"WASM_PACK_PATH": "wasm-pack",
"WASM_TARGET": "web",
"WASM_EXTENSION_MODE": "true"
}
}
}
}
Available Tools
| Tool | Description |
|---|---|
wasm_build | Build WASM module with wasm-pack |
wasm_optimize | Optimize WASM binary size |
wasm_validate | Validate WASM module |
wasm_inspect | Inspect WASM module exports |
Usage
// AI can invoke:
// wasm_build({ profile: 'release', target: 'web' })
// wasm_optimize({ input: 'module.wasm', level: 's' })
// wasm_validate({ path: 'module.wasm' })
Size Optimization
Build Flags
# Cargo.toml
[profile.release]
opt-level = "z" # Optimize for size (aggressive)
lto = "fat" # Full LTO
codegen-units = 1
panic = "abort" # No unwinding
strip = true
Post-Build Optimization
# Install wasm-opt (from binaryen)
brew install binaryen
# Optimize WASM binary
wasm-opt -Os -o output.wasm input.wasm
# Or with wasm-pack
wasm-pack build --release -- --features wee_alloc
wee_alloc (Smaller Allocator)
[dependencies]
wee_alloc = { version = "0.4", optional = true }
[features]
default = ["wee_alloc"]
#[cfg(feature = "wee_alloc")]
#[global_allocator]
static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT;
Common Use Cases
Cryptography
use wasm_bindgen::prelude::*;
use sha2::{Sha256, Digest};
#[wasm_bindgen]
pub fn hash_sha256(data: &[u8]) -> Vec<u8> {
let mut hasher = Sha256::new();
hasher.update(data);
hasher.finalize().to_vec()
}
Data Compression
use wasm_bindgen::prelude::*;
use flate2::Compression;
use flate2::write::GzEncoder;
use std::io::Write;
#[wasm_bindgen]
pub fn compress_gzip(data: &[u8]) -> Vec<u8> {
let mut encoder = GzEncoder::new(Vec::new(), Compression::default());
encoder.write_all(data).unwrap();
encoder.finish().unwrap()
}
JSON Parsing (Fast)
use wasm_bindgen::prelude::*;
use serde_json::Value;
#[wasm_bindgen]
pub fn parse_json(input: &str) -> JsValue {
match serde_json::from_str::<Value>(input) {
Ok(value) => serde_wasm_bindgen::to_value(&value).unwrap(),
Err(e) => JsValue::from_str(&format!("Error: {}", e))
}
}
Debugging
Source Maps
# Build with debug info
wasm-pack build --dev
# Or with custom flags
RUSTFLAGS="-C debuginfo=2" wasm-pack build
Browser DevTools
- Chrome: DevTools → Sources → shows .wasm files
- Firefox: Debugger → shows WASM as text
- Safari: Limited WASM debugging
Console Logging
use web_sys::console;
#[wasm_bindgen]
pub fn debug_log(message: &str) {
console::log_1(&message.into());
}
Quality Checklist
- WASM module builds with wasm-pack
- Cross-browser loading tested
- Memory usage within limits
- Service worker lifecycle handled
- Size optimized for extension bundle
- Error handling implemented
- Fallback for unsupported browsers
Similar Skills
Applies Anthropic's official brand colors and typography to any sort of artifact that may benefit from having Anthropic's look-and-feel. Use it when brand colors or style guidelines, visual formatting, or company design standards apply.
Creating algorithmic art using p5.js with seeded randomness and interactive parameter exploration. Use this when users request creating art using code, generative art, algorithmic art, flow fields, or particle systems. Create original algorithmic art rather than copying existing artists' work to avoid copyright violations.
Create beautiful visual art in .png and .pdf documents using design philosophy. You should use this skill when the user asks to create a poster, piece of art, design, or other static piece. Create original visual designs, never copying existing artists' work to avoid copyright violations.