Load Testing without the
Runtime Overhead.

Script in JavaScript. Execute in Rust.
The world's most efficient load testing engine. Sub-millisecond latency at 50,000 concurrent users.

$ cargo install fusillade
$ fusi run scenario.js -w 10000
[INFO] Spawning 10,000 workers...
[DONE] 215,618 RPS | P95: 231µs | 100% success

Raw Performance

Benchmarked at 50,000 concurrent virtual users

186K
Requests/sec
283µs
P95 Latency
400MB
Memory Usage
100%
Success Rate

Benchmarked on localhost against a simple Rust/Axum server. Real-world results will vary based on network latency and server complexity.

Why Rust?

Traditional load testing tools are built on garbage-collected runtimes. At high concurrency, GC pauses introduce latency spikes that pollute your metrics. Fusillade eliminates this with zero-allocation hot paths and predictable memory usage.

Thread-per-Worker

Each virtual user runs on a dedicated OS thread with its own QuickJS runtime. No async runtime contention. No coroutine scheduling overhead. Pure synchronous blocking I/O with predictable latency characteristics.

Memory Efficiency

50,000 concurrent users in ~400MB of RAM. Rust's ownership model eliminates memory leaks and fragmentation. No GC pauses means consistent sub-millisecond latencies even under extreme load.

Accurate Metrics

Latency measurements exclude JavaScript execution overhead—only network time and body reads are counted. HDR histograms capture precise percentiles without bucket quantization errors.

Write Tests in JavaScript

Familiar syntax. Full ES Module support. Powerful built-ins.

Load Test with Stages

export const options = {
  stages: [
    { duration: '30s', target: 100 },  // Ramp to 100 VUs
    { duration: '1m', target: 100 },   // Hold at peak
    { duration: '10s', target: 0 },    // Ramp down
  ],
  thresholds: {
    'http_req_duration': ['p95 < 500'],
    'http_req_failed': ['rate < 0.01'],
  }
};

export default function () {
  const res = http.post('https://api.example.com/checkout',
    JSON.stringify({ item_id: '12345' }),
    { headers: { 'Content-Type': 'application/json' } }
  );

  check(res, {
    'status is 200': (r) => r.status === 200,
    'response time OK': (r) => r.timings.duration < 500,
  });

  sleep(1);
}

Lifecycle Hooks

// Load test data once, share across all workers
const users = new SharedArray('users', () =>
  JSON.parse(open('./data/users.json'))
);

export function setup() {
  // Runs once before test starts
  const loginRes = http.post('/api/login', {
    username: 'admin', password: 'secret'
  });
  return { token: JSON.parse(loginRes.body).token };
}

export default function(data) {
  // Each worker receives setup data
  const user = users[__WORKER_ID % users.length];

  http.get('/api/profile', {
    headers: { 'Authorization': `Bearer ${data.token}` }
  });
}

export function teardown(data) {
  // Cleanup after all workers finish
  http.post('/api/logout');
}

Multi-Protocol Support

Test any backend with native protocol implementations

HTTP/1.1 & HTTP/2

const res = http.post(url, body, {
  headers: { 'Content-Type': 'application/json' },
  timeout: '10s',
  name: 'CreateOrder'
});
// res.proto → "h1" or "h2"

WebSockets

const socket = ws.connect('wss://api.example.com');
socket.send(JSON.stringify({ type: 'subscribe' }));
const msg = socket.recv();
socket.close();

gRPC

const client = new GrpcClient();
client.load(['./hello.proto'], ['./protos']);
client.connect('http://localhost:50051');
const res = client.invoke('helloworld.Greeter/SayHello', { name: 'World' });

MQTT

const mqtt = new JsMqttClient();
mqtt.connect('localhost', 1883, 'client-1');
mqtt.publish('sensors/temp', '22.5');
mqtt.close();

AMQP (RabbitMQ)

const amqp = new JsAmqpClient();
amqp.connect('amqp://localhost');
amqp.publish('', 'my-queue', JSON.stringify({ event: 'order.created' }));

Server-Sent Events

const client = sse.connect('/events');
const event = client.recv();
// { event: 'update', data: '...', id: '1' }
client.close();

Browser Automation (Chromium)

const browser = chromium.launch();
const page = browser.newPage();
page.goto('https://example.com/login');
page.type('#username', 'testuser');
page.type('#password', 'secret');
page.click('#submit');

const perf = page.metrics();
print(`DOM interactive: ${perf.domInteractive - perf.navigationStart}ms`);

const screenshot = page.screenshot();  // Auto-captured on check() failures
browser.close();

Flexible Execution

Multiple execution models for different testing scenarios

Load Profiles

  • Ramping VUs — Gradually scale workers up/down with stages
  • Constant Arrival Rate — Fixed RPS regardless of response time
  • Per-VU Iterations — Each worker runs N iterations then exits
  • Multiple Scenarios — Run different user behaviors concurrently

Multi-Scenario Example

export const options = {
  scenarios: {
    browse: {
      workers: 50,
      duration: '2m',
      exec: 'browseProducts',
    },
    checkout: {
      workers: 10,
      duration: '90s',
      exec: 'checkoutFlow',
      startTime: '30s',  // Delayed start
    },
  },
};

export function browseProducts() { ... }
export function checkoutFlow() { ... }

Distributed Load Generation

Scale horizontally across multiple machines or Kubernetes pods

Architecture

+-------------------------------------+
|         Controller (:9000)          |
|   Dashboard / Orchestration / DB    |
+-----------------+-------------------+
                  | gRPC
     +------------+------------+
     |            |            |
     v            v            v
+--------+   +--------+   +--------+
| Worker |   | Worker |   | Worker |
|  :8080 |   |  :8080 |   |  :8080 |
+--------+   +--------+   +--------+

Quick Start

# Start workers
fusillade worker --connect controller:9001

# Or run the controller
fusillade controller --listen 0.0.0.0:9000

# Dispatch via API
curl -X POST http://controller:9000/api/dispatch \
  -d '{"script_content": "...", "config": {...}}'

Kubernetes Native

  • • HorizontalPodAutoscaler scales workers 3 → 50 pods
  • • Automatic asset distribution (scripts + data files)
  • • Real-time dashboard at /dashboard
  • • SQLite history for performance trend analysis

Chaos Engineering Built-In

Test system resilience with native fault injection

Jitter Injection

Add artificial latency to simulate slow networks or upstream services

fusi run test.js --jitter 500ms

Request Dropping

Randomly fail requests to simulate packet loss or connection resets

fusi run test.js --drop 0.05

Config-Based

Define chaos parameters in your script options

export const options = {
  jitter: '250ms',
  drop: 0.03,
};

Developer Experience

Tools that fit into your existing workflow

HAR Recording

Record browser sessions and convert to test scripts

fusi record -o flow.js
fusi convert --input recording.har

TypeScript Support

Generate type definitions for IDE autocomplete

fusi types -o index.d.ts

CI/CD Integration

Export to JUnit XML, CSV, JSON, or OTLP

fusi run test.js --headless \
  --out junit=results.xml \
  --out csv=metrics.csv

Cost Estimation

Estimate bandwidth costs before running large tests

fusi run heavy.js --estimate-cost
# Est. AWS Cost: ~$0.22 (2.45 GB)

Smart Error Replay

Debug failed requests after a test run

# Export failures to cURL commands
fusi export fusillade-errors.json --format curl

# Replay failed requests
fusi replay fusillade-errors.json --parallel

Interactive Control

Control running tests in real-time

fusi run test.js --interactive
> ramp 100    # Scale to 100 workers
> pause       # Pause execution
> resume      # Resume
> status      # Show current stats

Batteries Included

Built-in utilities for common load testing needs

crypto

crypto.md5(data)
crypto.sha1(data)
crypto.sha256(data)
crypto.hmac('sha256', key, data)

encoding

encoding.b64encode('user:pass')
encoding.b64decode(token)

utils

utils.uuid()
utils.randomInt(1, 100)
utils.randomString(16)
utils.randomItem(users)

Custom Metrics

Track business-level metrics with thresholds

// Define thresholds in options
export const options = {
  thresholds: {
    'checkout_duration': ['p95 < 500'],
    'payment_success': ['rate > 0.99'],
  }
};

// Record custom metrics
metrics.histogramAdd('checkout_duration', Date.now() - start);
metrics.counterAdd('items_sold', 3);
metrics.rateAdd('payment_success', true);
metrics.gaugeSet('queue_depth', 42);

// Query stats programmatically
const s = stats.get('CreateOrder');
if (s.p95 > 500) print('Warning: P95 latency degrading');

Get Started in Seconds

$ cargo install fusillade
$ fusi types -o index.d.ts # IDE autocomplete
$ fusi run scenario.js -w 1000 -d 30s

~35MB binary. No runtime needed. Works on Linux, macOS, and Windows.