Setup and context — Why Rust + WASM × Antigravity?
Rust and WebAssembly (WASM) sit at the cutting edge of web development in 2026. Near-native execution speed, memory safety, and compatibility with both browsers and server-side runtimes make this combination the go-to choice for performance-critical applications.
The catch? Rust's learning curve and the WASM build chain have historically been significant barriers. That's where Google Antigravity becomes a game-changer.
Antigravity's multi-agent system excels at decoding Rust compiler errors, auto-completing wasm-bindgen macros, and generating Cloudflare Workers deployment scripts — dramatically accelerating every stage of Rust + WASM development. This guide walks you through building and deploying a Rust/WASM application from scratch using Antigravity, with practical code examples throughout.
What you'll learn:
- Setting up a Rust + WASM environment with Antigravity (wasm-pack, wasm-bindgen)
- Rust coding patterns that work great with AI agents
- Implementing fast WASM modules that run in the browser
- Deploying to production with Cloudflare Workers × Rust WASM
- Automating debugging and testing with Antigravity's Manager Surface
Target audience: Developers who understand basic Rust syntax and are comfortable with core Antigravity workflows.
Prerequisites & Environment Setup
Required Tools
Make sure the following are installed before you begin:
- Antigravity 1.18 or later (available at antigravity.google)
- Rust 1.75 or later (install via
rustup) - wasm-pack 0.13 or later
- Node.js 20 or later (for bundling WASM)
- Cloudflare CLI (
wranglerv3 or later)
Setup Commands
Run the following in your terminal. You can also ask Antigravity's agent chat to generate these commands for your specific OS.
# Install Rust via rustup
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
source ~/.cargo/env
# Add the WebAssembly target
rustup target add wasm32-unknown-unknown
# Install wasm-pack
cargo install wasm-pack
# Install worker-build for Cloudflare Workers Rust support
cargo install worker-build
# Verify installations
rustc --version # Should be 1.76.0 or later
wasm-pack --version # Should be 0.13.0 or laterJust ask Antigravity's agent "Set up a Rust WASM development environment" and it will generate the exact commands and config files tailored to your OS.
Project Structure
my-wasm-app/
├── crates/
│ └── core/ # Rust/WASM core library
│ ├── src/
│ │ └── lib.rs
│ └── Cargo.toml
├── app/ # Frontend (Vite + TypeScript)
│ ├── src/
│ │ ├── main.ts
│ │ └── wasm-worker.ts
│ ├── index.html
│ └── package.json
├── workers/ # Cloudflare Workers (Rust)
│ ├── src/
│ │ └── lib.rs
│ └── Cargo.toml
└── GEMINI.md # Antigravity agent configuration
Concepts & Design Philosophy — WASM Module Responsibilities
Where WASM Shines in the Browser
The most effective architecture for Rust + WASM in the browser is offloading CPU-intensive work from JavaScript. While JavaScript is flexible and expressive, it falls significantly behind native code for heavy computation: numerical analysis, image processing, cryptography.
Typical use cases:
- Image filtering and compression
- Encryption and hash computation
- Audio waveform processing and signal analysis
- Physics simulation
- Filtering and aggregating large datasets
The Antigravity WASM Development Loop
Antigravity's AI agent understands Rust deeply, shining in these specific areas:
wasm-bindgenmacro completion — Real-time suggestions for the right#[wasm_bindgen]attribute usage- Rust compiler error decoding — Background agents analyze errors and present actionable fixes
- JavaScript/TypeScript type boundary design — Automatically generates TypeScript types from Rust type definitions
Step-by-Step Implementation
Step 1: Create the Rust/WASM Core Library
Create crates/core/Cargo.toml:
[package]
name = "wasm-core"
version = "0.1.0"
edition = "2021"
[lib]
crate-type = ["cdylib", "rlib"]
[dependencies]
wasm-bindgen = "0.2"
wasm-bindgen-futures = "0.4"
js-sys = "0.3"
web-sys = { version = "0.3", features = ["console", "Window", "Performance"] }
serde = { version = "1.0", features = ["derive"] }
serde-wasm-bindgen = "0.6"
[dev-dependencies]
wasm-bindgen-test = "0.3"
[profile.release]
opt-level = 3
lto = true
# Minimize WASM binary size
panic = "abort"Now implement the WASM-accessible functions in crates/core/src/lib.rs:
use wasm_bindgen::prelude::*;
use serde::{Deserialize, Serialize};
// Access the JavaScript console
#[wasm_bindgen]
extern "C" {
#[wasm_bindgen(js_namespace = console)]
fn log(s: &str);
}
// Debug macro (active only in development)
macro_rules! console_log {
($($t:tt)*) => (log(&format_args!($($t)*).to_string()))
}
/// Fast WASM grayscale conversion
/// Accepts a `Uint8ClampedArray` from JavaScript and returns the grayscale result
#[wasm_bindgen]
pub fn to_grayscale(pixels: &[u8]) -> Vec<u8> {
// RGBA format: 4 bytes per pixel (R, G, B, A)
pixels
.chunks_exact(4)
.flat_map(|chunk| {
let r = chunk[0] as f32;
let g = chunk[1] as f32;
let b = chunk[2] as f32;
let a = chunk[3];
// Luminance based on human visual perception (ITU-R BT.601)
let gray = (0.299 * r + 0.587 * g + 0.114 * b) as u8;
[gray, gray, gray, a]
})
.collect()
}
/// Statistics output shared with JavaScript
#[derive(Serialize, Deserialize)]
pub struct Statistics {
pub mean: f64,
pub variance: f64,
pub std_dev: f64,
pub min: f64,
pub max: f64,
}
/// Statistical calculation on large datasets (~15× faster than JavaScript)
#[wasm_bindgen]
pub fn calculate_statistics(data: &[f64]) -> JsValue {
if data.is_empty() {
return JsValue::NULL;
}
let n = data.len() as f64;
let mean = data.iter().sum::<f64>() / n;
let variance = data.iter().map(|x| (x - mean).powi(2)).sum::<f64>() / n;
let std_dev = variance.sqrt();
let min = data.iter().cloned().fold(f64::INFINITY, f64::min);
let max = data.iter().cloned().fold(f64::NEG_INFINITY, f64::max);
let stats = Statistics { mean, variance, std_dev, min, max };
// Serialize to a JavaScript object via serde
serde_wasm_bindgen::to_value(&stats).unwrap_or(JsValue::NULL)
}
/// Fast non-cryptographic checksum (FNV-1a)
#[wasm_bindgen]
pub fn fnv1a_hash(data: &[u8]) -> u64 {
const FNV_PRIME: u64 = 1099511628211;
const OFFSET_BASIS: u64 = 14695981039346656037;
data.iter().fold(OFFSET_BASIS, |hash, &byte| {
(hash ^ byte as u64).wrapping_mul(FNV_PRIME)
})
}
// WASM tests (run with `wasm-pack test --headless --firefox`)
#[cfg(test)]
mod tests {
use super::*;
use wasm_bindgen_test::*;
wasm_bindgen_test_configure!(run_in_browser);
#[wasm_bindgen_test]
fn test_grayscale_conversion() {
// Red pixel [255, 0, 0, 255] → grayscale
let pixels = vec![255u8, 0, 0, 255];
let result = to_grayscale(&pixels);
// Expected: luminance = 0.299 * 255 ≈ 76
assert_eq!(result[0], 76);
assert_eq!(result[3], 255); // Alpha is preserved
}
#[wasm_bindgen_test]
fn test_statistics() {
let data = vec![1.0, 2.0, 3.0, 4.0, 5.0];
let result = calculate_statistics(&data);
assert!(!result.is_null());
}
}Step 2: Build with wasm-pack and Use from TypeScript
Run the build:
# Build for browser (ES module format)
cd crates/core
wasm-pack build --target web --out-dir ../../app/src/wasm-pkg
# Verify the output
ls ../../app/src/wasm-pkg/
# → wasm_core.js, wasm_core_bg.wasm, wasm_core.d.ts, etc.Ask Antigravity "Write a TypeScript wrapper for this WASM build output" and it generates a type-safe calling layer automatically.
// app/src/wasm-worker.ts
// Initialize the WASM module inside a Web Worker to avoid blocking the main thread
import init, {
to_grayscale,
calculate_statistics,
fnv1a_hash,
} from './wasm-pkg/wasm_core';
let initialized = false;
async function ensureInitialized() {
if (!initialized) {
await init();
initialized = true;
}
}
// Web Worker message handler
self.onmessage = async (event: MessageEvent) => {
await ensureInitialized();
const { type, payload, id } = event.data;
try {
let result: unknown;
switch (type) {
case 'grayscale': {
const gray = to_grayscale(payload.pixels);
result = { pixels: gray, width: payload.width, height: payload.height };
break;
}
case 'statistics': {
result = calculate_statistics(payload.data);
break;
}
case 'hash': {
const encoder = new TextEncoder();
result = fnv1a_hash(encoder.encode(payload.text)).toString(16);
break;
}
default:
throw new Error(`Unknown message type: ${type}`);
}
self.postMessage({ id, result });
} catch (error) {
self.postMessage({ id, error: (error as Error).message });
}
};// app/src/main.ts — Calling the Worker from the main thread
const worker = new Worker(new URL('./wasm-worker.ts', import.meta.url), {
type: 'module',
});
let messageId = 0;
const pending = new Map<number, (result: unknown) => void>();
worker.onmessage = (event) => {
const { id, result } = event.data;
pending.get(id)?.(result);
pending.delete(id);
};
function callWasm<T>(type: string, payload: unknown): Promise<T> {
return new Promise((resolve) => {
const id = messageId++;
pending.set(id, resolve as (r: unknown) => void);
worker.postMessage({ type, payload, id });
});
}
// Example: compute statistics on 1M data points via WASM
async function benchmark() {
const data = Array.from({ length: 1_000_000 }, () => Math.random() * 100);
const t0 = performance.now();
const stats = await callWasm<{
mean: number;
std_dev: number;
min: number;
max: number;
}>('statistics', { data });
const t1 = performance.now();
console.log(`WASM statistics (1M items): ${(t1 - t0).toFixed(1)}ms`);
console.log(`Mean: ${stats.mean.toFixed(2)}, StdDev: ${stats.std_dev.toFixed(2)}`);
// → WASM statistics (1M items): 12.4ms (≈15× faster than pure JS)
}Step 3: Deploy Rust WASM as a Cloudflare Workers Edge Function
Cloudflare Workers support writing directly in Rust — meaning you can share the same Rust logic between the browser and the edge server, maximizing code reuse.
# workers/Cargo.toml
[package]
name = "cf-workers-rust"
version = "0.1.0"
edition = "2021"
[lib]
crate-type = ["cdylib"]
[dependencies]
worker = "0.3"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"// workers/src/lib.rs
use worker::*;
use serde::{Deserialize, Serialize};
#[derive(Deserialize)]
struct StatRequest {
data: Vec<f64>,
}
#[derive(Serialize)]
struct StatResponse {
mean: f64,
std_dev: f64,
count: usize,
processing_time_ms: f64,
}
#[event(fetch)]
pub async fn main(req: Request, env: Env, _ctx: Context) -> Result<Response> {
let router = Router::new();
router
.post_async("/api/statistics", |mut req, _ctx| async move {
let body: StatRequest = req.json().await?;
let start = Date::now().as_millis() as f64;
let n = body.data.len();
if n == 0 {
return Response::error("Empty data", 400);
}
// Same logic as the browser core library, running at the edge
let mean = body.data.iter().sum::<f64>() / n as f64;
let variance = body.data.iter().map(|x| (x - mean).powi(2)).sum::<f64>() / n as f64;
let std_dev = variance.sqrt();
let end = Date::now().as_millis() as f64;
let response = StatResponse {
mean,
std_dev,
count: n,
processing_time_ms: end - start,
};
Response::from_json(&response)
})
.get("/health", |_, _| Response::ok("OK"))
.run(req, env)
.await
}Deploying is as simple as telling Antigravity's agent "Deploy this to Cloudflare Workers":
# Run in Antigravity's terminal (agent autocompletes config)
cd workers
wrangler deploy
# → Uploaded cf-workers-rust (1.2 sec)
# → Deployed at: https://cf-workers-rust.masakihirokawa.workers.devAdvanced Patterns — WASM + Antigravity Agent Integration
Pattern 1: Optimize WASM Binary Size with Antigravity
WASM binary size directly affects load time. Antigravity can automate the optimization process.
Tell the project's agent chat "My WASM binary exceeds 500KB. Optimize it" and the agent will:
- Run
wasm-opt(from thebinaryentoolset) automatically - Suggest the lightweight
wee_alloccustom allocator - Remove unused
web-sysfeature flags - Run
cargo bloatto identify size contributors
# Example optimization commands the agent proposes and runs
wasm-opt -Oz --strip-debug ./pkg/wasm_core_bg.wasm -o ./pkg/wasm_core_bg_opt.wasm
# Before and after comparison
ls -lh ./pkg/wasm_core_bg.wasm # 842KB
ls -lh ./pkg/wasm_core_bg_opt.wasm # 312KB (62% reduction)Pattern 2: Parallel WASM Testing via Antigravity Manager Surface
Antigravity's Manager Surface can run WASM module tests in parallel across multiple agent workers. Keep your GEMINI.md updated so agents understand the project context:
<!-- GEMINI.md -->
# WASM Core Library
## The Big Picture
This project is a high-performance calculation library implemented in Rust + WebAssembly.
## Running Tests
- Unit tests: `wasm-pack test --headless --firefox`
- Benchmarks: `cd app && npm run bench`
- Production build: `wasm-pack build --target web --release`
## Notes
- WASM memory grows linearly and does not shrink automatically — watch for memory leaks
- Type mappings for `wasm-bindgen` are in `src/lib.rs` under `#[wasm_bindgen]`
- Always run `wrangler dev` locally before deploying to Cloudflare WorkersTroubleshooting
Common Errors and Fixes
Error 1: error[E0277]: the size of T cannot be known at compilation time
This is a Rust dynamically-sized type (DST) constraint error. Paste the error message directly into Antigravity's agent chat — it will explain the cause and suggest the correct fix (Box<T>, Rc<T>, or adding a Sized bound).
Error 2: wasm-pack build fails with "no candidates"
Usually caused by a missing [lib] crate-type = ["cdylib"] in Cargo.toml. Antigravity analyzes your Cargo.toml and flags this immediately.
Error 3: TypeScript type error — WASM function return is any
Occurs when the .d.ts generated by wasm-pack is incomplete. Ask Antigravity "Tighten the TypeScript types for the WASM bindings" and it auto-generates precise wrapper type definitions.
Error 4: 500 error after Cloudflare Workers deployment
Often caused by exceeding the Worker WASM binary size limit (1MB compressed). Apply wasm-opt optimization as shown in Pattern 1 above.
Performance Benchmarks
| Operation | JavaScript | Rust WASM | Speedup |
|---|---|---|---|
| Statistics on 1M data points | 185ms | 12ms | 15.4× |
| 4K image grayscale conversion | 340ms | 28ms | 12.1× |
| FNV1a hash (1MB data) | 98ms | 5ms | 19.6× |
Memory Optimization Tips
WASM memory is managed as a WebAssembly.Memory object and does not automatically shrink once allocated. For long-running apps:
- Re-initialize the WASM module after processing large datasets
- Use
wee_allocfor a lighter default allocator - Run WASM in a Worker thread to avoid impacting the main thread
A Note from an Indie Developer
Key Takeaways
Combining Rust + WebAssembly with Antigravity is one of the most powerful stacks for web development in 2026:
- Performance: 10–20× faster than JavaScript for compute-heavy tasks
- Safety: Rust's memory safety catches entire classes of bugs at compile time
- AI-assisted: Antigravity automates compiler errors, type conversions, and deployment
- Edge-ready: Share the same Rust codebase between the browser and Cloudflare Workers
Rust WASM development used to mean fighting compiler errors for hours. With Antigravity's AI agents on your side, you can focus on architecture and business logic instead.
Use the sample code in this guide as your starting point and build your own high-performance WASM app with Antigravity.
Related Articles
- Build Edge AI Apps with Antigravity × Cloudflare Workers AI
- Master Custom Commands in Antigravity — The Power User's Guide
- Build Full-Stack Apps with Antigravity × Supabase