forge-runtime
v0.1.7
Published
Experimental browser runtime built on top of WebAssembly, Workers, SharedArrayBuffer, Atomics, and IndexedDB.
Maintainers
Readme
forge-runtime
Environment Support
Forge Runtime is currently focused on modern browsers and browser runtime experimentation. | Feature | Browser | Node.js | |---------------------------------|---------|---------| | Memory APIs | ✅ | ⚠️ Experimental | | Worker Pool | ✅ | ❌ | | Virtual Filesystem (IndexedDB) | ✅ | ❌ | | Shared Memory | ✅ | ❌ | | Atomics | ✅ | ❌ | | Channels | ✅ | ❌ |
A systems toolkit for building advanced web applications with runtime-inspired primitives.
forge-runtime provides a collection of low-level APIs built on top of modern browser technologies such as WebAssembly, Web Workers, SharedArrayBuffer, Atomics, MessageChannel, and IndexedDB.
The goal is to make runtime, concurrency, storage, and systems-programming concepts accessible directly inside the browser.
Features
- WebAssembly-backed memory management
- Virtual filesystem powered by IndexedDB
- Worker-based task execution
- Shared memory support
- Atomic operations
- Message channels
- Queue primitives
- TypeScript support
- Modern ESM package
Installation
npm install forge-runtimeforge-runtime
Manual memory management for JavaScript powered by WebAssembly.
forge-runtime brings a familiar C-style memory model to JavaScript. Allocate raw memory blocks, work directly with bytes through TypedArrays, and explicitly free memory when you're done.
Why?
JavaScript normally relies on garbage collection.
Sometimes, especially when working with:
- WebAssembly
- Large binary files
- Video processing
- Audio processing
- Custom runtimes
- Binary protocols
- Low-level systems experiments
it can be useful to manage memory manually.
forge-runtime provides a simple API inspired by C's malloc() and free() while remaining fully usable from JavaScript.
Basic Usage
import { allocMemory, freeMemory } from "forge-runtime";
const block = allocMemory(100);
block.memory[0] = 65;
block.memory[1] = 66;
block.memory[2] = 67;
freeMemory(block);Memory Block
Calling:
const block = allocMemory(100);returns:
{
ptr: 1024,
size: 100,
memory: Uint8Array(...)
}Properties
| Property | Description | | -------- | ----------------------------------------- | | ptr | Pointer/address inside WebAssembly memory | | size | Allocated size in bytes | | memory | Uint8Array view over the allocated memory |
Large Memory Allocations
forge-runtime is configured with 1 GB of WebAssembly memory, allowing applications to work with large binary datasets and memory-intensive workloads.
Examples:
const block = allocMemory(1024 * 1024 * 100); // 100 MBconst block = allocMemory(1024 * 1024 * 1024); // 1 GBCheck the current WebAssembly memory size:
console.log(block.memory.buffer.byteLength);Typical use cases:
- Large file processing
- Video and audio pipelines
- WebAssembly runtimes
- Binary protocols
- Custom allocators
- Systems programming experiments
Notes
- Actual usable memory depends on available system resources.
- WebAssembly memory is backed by virtual memory and may not immediately consume physical RAM.
- Extremely large allocations may still fail if insufficient memory is available.
- Always free memory when finished.
Writing Data
const block = allocMemory(4);
block.memory[0] = 10;
block.memory[1] = 20;
block.memory[2] = 30;
block.memory[3] = 40;Reading Data
console.log(block.memory[0]);
console.log(block.memory[1]);Working With Strings
const block = allocMemory(100);
const bytes = new TextEncoder().encode("hello");
block.memory.set(bytes);
const text = new TextDecoder().decode(block.memory.subarray(0, bytes.length));
console.log(text);Output:
helloFreeing Memory
freeMemory(block);After freeing:
block.ptr === null;
block.memory === null;
block.size === 0;This helps prevent accidental use-after-free bugs.
Important Notes
Memory Is Not Erased
Calling:
freeMemory(block);does not immediately erase bytes.
It marks the memory as available for future allocations.
Do Not Use Freed Blocks
Bad:
freeMemory(block);
block.memory[0] = 123;Good:
block.memory[0] = 123;
freeMemory(block);Ownership vs Data
When memory is freed:
freeMemory(block);ownership of the memory is released, but the old bytes may still remain in memory until they are overwritten by a future allocation.
This behavior is similar to C's:
free(ptr);and is one of the most important concepts in manual memory management.
TypeScript Support
forge-runtime ships with built-in TypeScript definitions.
import { allocMemory, freeMemory } from "forge-runtime";
const block = allocMemory(100);
block.memory[0] = 123;
freeMemory(block);Inspiration
forge-runtime is inspired by:
void* ptr = malloc(size);
/* use memory */
free(ptr);and brings a similar workflow to JavaScript through WebAssembly.
Educational Purpose
This project was created to help JavaScript developers better understand:
- Memory allocation
- Pointers
- Heaps
- WebAssembly memory
- Manual memory management
- Systems programming concepts
through a simple JavaScript API.
Quick Start
import {
allocMemory,
freeMemory,
writeText,
readText,
spawn,
} from "forge-runtime";
const block = allocMemory(1024);
block.memory[0] = 65;
console.log(block.memory[0]);
freeMemory(block);
await writeText("/hello.txt", "Hello Forge");
console.log(await readText("/hello.txt"));
const result = await spawn((x) => x * 2, 10);
console.log(result);Forge Pool
A lightweight Worker Pool for running CPU-intensive JavaScript tasks in parallel using Web Workers.
Features
- Worker Pool
- Task Queue
- Automatic Scheduling
- Promise-based API
- Async Function Support
- Error Handling
- Worker Reuse
- Automatic Load Distribution
- Pool Cleanup
Installation
Copy createPool.js into your project.
import { createPool } from "forge-runtime";
const pool = createPool(4);Create a Pool
import { createPool } from "forge-runtime";
const pool = createPool(4);Creates a pool with 4 workers.
Run a Task
const result = await pool.run((n) => n * 2, 10);
console.log(result);Output:
20Run Multiple Tasks
const p1 = pool.run((n) => n * 2, 10);
const p2 = pool.run((n) => n * 3, 20);
const p3 = pool.run((n) => n * 4, 30);
const results = await Promise.all([p1, p2, p3]);
console.log(results);Output:
[20, 60, 120]Async Functions
const result = await pool.run(
async (n) => {
await new Promise((resolve) => setTimeout(resolve, 1000));
return n * 2;
},
10,
);Output:
20Array Processing
const result = await pool.run(
(arr) => arr.reduce((a, b) => a + b, 0),
[1, 2, 3, 4, 5],
);
console.log(result);Output:
15Error Handling
try {
await pool.run(() => {
throw new Error("Boom");
});
} catch (error) {
console.error(error.message);
}Output:
BoomPool Information
Pool Size
console.log(pool.size);Pending Tasks
console.log(pool.pendingTasks);Destroy Pool
Terminate all workers and clean resources.
pool.destroy();How It Works
User
│
▼
pool.run()
│
▼
Task Queue
│
▼
Scheduler
│
▼
Available Worker
│
▼
Worker Executes Task
│
▼
Result Returned
│
▼
Promise ResolvedNotes
Functions passed to run() must be self-contained.
✅ Works:
await pool.run((n) => n * 2, 10);❌ Does not work:
const multiplier = 5;
await pool.run((n) => n * multiplier, 10);Reason:
Workers execute the function in an isolated context and do not have access to variables from the main thread.
Use Cases
- Large Calculations
- Data Processing
- Sorting
- Searching
- Hashing
- Simulations
- Image Processing
- CPU-intensive Tasks
Example
import { createPool } from "forge-runtime";
const pool = createPool(4);
const result = await pool.run((n) => n * n, 12);
console.log(result);
pool.destroy();Output:
144Built for learning browser concurrency, worker pools, scheduling, and runtime architecture.
Philosophy
Modern browsers expose powerful low-level primitives:
- WebAssembly
- Web Workers
- SharedArrayBuffer
- Atomics
- IndexedDB
- MessageChannel
forge-runtime provides a unified API layer on top of these primitives, making it easier to experiment with runtime architecture, concurrency, memory management, storage systems, and browser systems programming.
Memory
Allocate and free memory manually through WebAssembly-backed memory.
import { allocMemory, freeMemory } from "forge-runtime";
const block = allocMemory(1024);
block.memory[0] = 65;
freeMemory(block);Memory block:
{
ptr: 1024,
size: 1024,
memory: Uint8Array(...)
}Shared WebAssembly Pool
Run CPU-intensive JavaScript and WebAssembly memory operations across a pool of Web Workers using a shared WebAssembly.Memory.
Features
- Shared
WebAssembly.Memory - Zero-copy memory access
- Worker pool execution
- Pointer-based memory blocks
- Parallel processing
- WebAssembly-backed allocator
- String ↔ memory helpers
- No message-passing of large buffers
Installation
import { createHeap, memory, createPoolWasm } from "forge-runtime";Creating a Heap
const heap = await createHeap();The heap provides:
heap.alloc(size);
heap.free(block);
heap.fromString(text);
heap.toString(block);Allocating Memory
const block = heap.alloc(1);
block.memory[0] = 77;
console.log(block.ptr);
console.log(block.memory[0]);Example:
73360
77Creating a Worker Pool
const pool = await createPoolWasm(memory, 4);Creates four workers sharing the same WebAssembly memory.
Running Tasks
const result = await pool.run(
(x) => x * 2,
5,
);
console.log(result);Output:
10Working with Shared Memory
Allocate memory:
const block = heap.alloc(1);
block.memory[0] = 77;Read it from a worker:
const result = await pool.runHeap(
({ ptr, HEAP }) => {
return HEAP[ptr];
},
block,
);
console.log(result);Output:
77No memory is copied between threads.
Workers access the same underlying shared memory.
Processing Large Buffers
const size = 100_000_000;
const block = heap.alloc(size);
block.memory.fill(1);Parallel update:
await Promise.all([
pool.runHeap(
({ ptr, size, HEAP }) => {
const end = ptr + size / 4;
for (let i = ptr; i < end; i++) {
HEAP[i] *= 2;
}
},
block,
),
]);This pattern allows large memory regions to be processed in parallel.
String Utilities
Store a string:
const block = heap.fromString("Hello World");Read it back:
const text = heap.toString(block);
console.log(text);Output:
Hello WorldAPI
createHeap()
Creates a WebAssembly-backed heap.
const heap = await createHeap();Returns:
{
(memory, HEAP, alloc, free, fromString, toString);
}createPoolWasm(memory, size)
Creates a pool of workers sharing the same WebAssembly memory.
const pool = await createPoolWasm(memory, 4);pool.run(handler, data)
Runs a task in a worker.
const result = await pool.run(
(x) => x * 2,
5,
);pool.runHeap(handler, block)
Runs a task against a shared memory block.
await pool.runHeap(
({ ptr, size, HEAP }) => {
// access shared memory
},
block,
);pool.destroy()
Stops all workers.
pool.destroy();Architecture
Main Thread
│
▼
Shared WebAssembly.Memory
│
┌────┼────┐
▼ ▼ ▼
W1 W2 W3 ...
│
▼
Shared HEAPU8Workers operate directly on the same memory buffer, allowing high-performance parallel processing without copying large arrays between threads.
Use Cases
- Image processing
- Video processing
- Audio processing
- Compression
- Parsing large files
- Scientific computing
- Data transformation
- Game engines
- WebAssembly runtimes
- Custom memory-managed applications
ArrayBuffer Utilities
Store an ArrayBuffer directly in shared WebAssembly memory:
const response = await fetch("https://jsonplaceholder.typicode.com/todos/1");
const buffer = await response.arrayBuffer();
const block = heap.fromArrayBuffer(buffer);This is equivalent to:
const block = heap.alloc(buffer.byteLength);
block.memory.set(new Uint8Array(buffer));but removes the boilerplate.
Response Utilities
Store a Fetch Response directly in shared WebAssembly memory:
const block = await heap.fromResponse(
await fetch("https://jsonplaceholder.typicode.com/todos/1"),
);Internally:
Response
↓
ArrayBuffer
↓
Shared WebAssembly MemoryThis allows large responses to be processed by workers without repeatedly transferring data between threads.
Real World Example
Process a JSON response inside a worker without blocking the main thread.
const heap = await createHeap();
const pool = await createPoolWasm(memory, 4);
const block = await heap.fromResponse(
await fetch("https://jsonplaceholder.typicode.com/todos/1"),
);
const title = await pool.runHeap(
({ ptr, size, HEAP }) => {
const text = new TextDecoder().decode(
Uint8Array.from(HEAP.subarray(ptr, ptr + size)),
);
const json = JSON.parse(text);
return json.title;
},
block,
);
console.log(title);Output:
delectus aut autemWhy use this?
Traditional processing:
Fetch
↓
Main Thread
↓
JSON.parse
↓
UI may freezeShared WebAssembly Pool:
Fetch
↓
Shared WebAssembly Memory
↓
Worker Pool
↓
JSON.parse
↓
ResultBenefits:
- Keeps the main thread responsive
- Avoids repeatedly copying large payloads
- Works with large JSON files, CSV files, logs, binary files, images, and other datasets
- Enables parallel processing across multiple workers
Virtual Filesystem
Store files using browser storage.
Write Text
import { writeText } from "forge-runtime";
await writeText("/hello.txt", "Hello Forge");Read Text
import { readText } from "forge-runtime";
const text = await readText("/hello.txt");
console.log(text);Write Binary Data
import { writeFile } from "forge-runtime";
await writeFile("/image.bin", new Uint8Array([1, 2, 3]));Read Binary Data
import { readFile } from "forge-runtime";
const file = await readFile("/image.bin");Check Existence
import { exists } from "forge-runtime";
const found = await exists("/hello.txt");Delete File
import { deleteFile } from "forge-runtime";
await deleteFile("/hello.txt");List Files
import { listFiles } from "forge-runtime";
const files = await listFiles();
console.log(files);Tasks
Execute work inside isolated workers.
import { spawn } from "forge-runtime";
const result = await spawn((x) => x * 2, 10);
console.log(result);Output:
20Channels
Create communication channels.
import { createChannel } from "forge-runtime";
const channel = createChannel();
channel.port1.postMessage("hello");
channel.port2.onmessage = (event) => {
console.log(event.data);
};SharedArrayBuffer Requirements
Some Forge Runtime features such as:
- Shared Memory
- Atomics
- Shared Queues
- Advanced Worker Communication
rely on SharedArrayBuffer.
For security reasons, modern browsers only expose SharedArrayBuffer in cross-origin isolated environments.
If you see an error such as:
ReferenceError: SharedArrayBuffer is not definedyour application is likely not running in a cross-origin isolated context.
Required Headers
Your server must send:
Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Embedder-Policy: require-corpVite Example
export default {
server: {
headers: {
"Cross-Origin-Opener-Policy": "same-origin",
"Cross-Origin-Embedder-Policy": "require-corp",
},
},
};Verify Support
console.log(typeof SharedArrayBuffer);
console.log(crossOriginIsolated);Expected output:
function
trueIf SharedArrayBuffer is undefined or crossOriginIsolated is false, browser security restrictions are preventing access to shared memory APIs.
Shared Memory
Create memory accessible from multiple workers.
import { createSharedMemory, createInt32View } from "forge-runtime";
const memory = createSharedMemory(1024);
const view = createInt32View(memory);
view[0] = 123;Atomic Operations
Safe concurrent access to shared memory.
import { load, store, add } from "forge-runtime";
store(view, 0, 10);
add(view, 0, 5);
console.log(load(view, 0));Output:
15Queues
Experimental shared-memory queue implementation.
import { createQueue, push, pop } from "forge-runtime";
const queue = createQueue();
push(queue, 10);
push(queue, 20);
console.log(pop(queue));
console.log(pop(queue));Output:
10
20TypeScript
Forge Runtime ships with TypeScript definitions.
import { allocMemory, createQueue, spawn } from "forge-runtime";Autocomplete and type checking work out of the box.
What forge-runtime Is
- Browser systems toolkit
- Runtime experimentation platform
- Collection of low-level browser primitives
- Educational systems-programming project
- Foundation for exploring runtime architecture
What forge-runtime Is Not
- Node.js replacement
- Bun replacement
- Server runtime
- Production operating system
Browser Support
Requires modern browsers with support for:
- ES Modules
- WebAssembly
- Web Workers
- SharedArrayBuffer
- Atomics
- IndexedDB
- MessageChannel
Roadmap
v0.1
- Memory
- Filesystem
- Tasks
v0.2
- Channels
- Shared Memory
- Atomics
v0.3
- Worker Pools
- Scheduler
- Job Queues
v0.4
- Streams
v0.5
- Database Layer
v1.0
- Browser Runtime Platform
License
MIT
