A tiny toolkit for heavy computations using Web Workers
Integration with React hooks and WASM
π Documentation β’ Live Demo β’ Getting Started β’ Examples β’ API β’ React Hooks β’ WASM β’ π€ MCP Server
- π Worker pool : Automatic load balancing across CPU cores
- βοΈ React-first : Provides hooks like
useComputewith loading, error, and progress states - π¦ WASM integration : Easily load and call AssemblyScript/Rust WASM modules
- π Non-blocking : Everything runs in Web Workers
- π§ Zero config : No manual worker files or postMessage handlers
- π¦ Tiny : Core library is ~5KB gzipped
- π― TypeScript : Full type safety with typed registry for autocomplete and compile-time checks
- π Progress tracking : Built-in progress reporting for long-running tasks
You can use Web Workers and WASM without a library. But here's the reality:
| Task | Without ComputeKit | With ComputeKit |
|---|---|---|
| Web Worker setup | Create separate .js files, handle postMessage, manage callbacks |
kit.register('fn', myFunc) |
| WASM loading | Fetch, instantiate, memory management, glue code | await loadWasmModule('/my.wasm') |
| React integration | Manual state, effects, cleanup, abort handling | useCompute() hook |
| Worker pooling | Build your own pool, queue, and load balancer | Built-in |
| TypeScript | Tricky worker typing, no WASM types | Full type inference |
| Error handling | Try-catch across message boundaries | Automatic with React error states |
ComputeKit's unique value: The only library that combines React hooks + WASM + Worker pool into one cohesive, type-safe developer experience.
| β Use ComputeKit | β Don't use ComputeKit |
|---|---|
| Image/video processing | Simple DOM updates |
| Data transformations (100K+ items) | Small array operations |
| Mathematical computations | API calls (use native fetch) |
| Parsing large files | String formatting |
| Cryptographic operations | UI state management |
| Real-time data analysis | Small form validations |
# npm
npm install @computekit/core
# With React bindings
npm install @computekit/core @computekit/react
# pnpm
pnpm add @computekit/core @computekit/react
# yarn
yarn add @computekit/core @computekit/reactimport { ComputeKit } from '@computekit/core';
// 1. Create a ComputeKit instance
const kit = new ComputeKit();
// 2. Register a compute function
kit.register('fibonacci', (n: number) => {
if (n <= 1) return n;
let a = 0,
b = 1;
for (let i = 2; i <= n; i++) {
[a, b] = [b, a + b];
}
return b;
});
// 3. Run it (non-blocking!)
const result = await kit.run('fibonacci', 50);
console.log(result); // 12586269025 : UI never froze!import { ComputeKitProvider, useComputeKit, useCompute } from '@computekit/react';
import { useEffect } from 'react';
// 1. Wrap your app with the provider
function App() {
return (
<ComputeKitProvider>
<AppContent />
</ComputeKitProvider>
);
}
// 2. Register functions at the app level
function AppContent() {
const kit = useComputeKit();
useEffect(() => {
// Register your compute functions once
kit.register('fibonacci', (n: number) => {
if (n <= 1) return n;
let a = 0,
b = 1;
for (let i = 2; i <= n; i++) {
[a, b] = [b, a + b];
}
return b;
});
}, [kit]);
return <Calculator />;
}
// 3. Use the hook in any component
function Calculator() {
const { data, loading, error, run } = useCompute<number, number>('fibonacci');
return (
<div>
<button onClick={() => run(50)} disabled={loading}>
{loading ? 'Computing...' : 'Calculate Fibonacci(50)'}
</button>
{data && <p>Result: {data}</p>}
{error && <p>Error: {error.message}</p>}
</div>
);
}This is where ComputeKit shines : combining useCompute with WASM for native-speed performance:
import { ComputeKitProvider, useComputeKit, useCompute } from '@computekit/react';
import { useEffect, useRef } from 'react';
import { loadWasm } from './wasmLoader'; // Your WASM loader
// 1. Wrap your app
function App() {
return (
<ComputeKitProvider>
<ImageProcessor />
</ComputeKitProvider>
);
}
// 2. Register a WASM-powered compute function
function ImageProcessor() {
const kit = useComputeKit();
const canvasRef = useRef<HTMLCanvasElement>(null);
useEffect(() => {
// Register a function that uses WASM internally
kit.register(
'blurImage',
async (input: {
data: number[];
width: number;
height: number;
passes: number;
}) => {
const wasm = await loadWasm();
const { data, width, height, passes } = input;
// Copy input to WASM memory
const ptr = wasm.getBufferPtr();
const wasmMem = new Uint8ClampedArray(wasm.memory.buffer, ptr, data.length);
wasmMem.set(data);
// Run WASM blur
wasm.blurImage(width, height, passes);
// Return result
return Array.from(new Uint8ClampedArray(wasm.memory.buffer, ptr, data.length));
}
);
}, [kit]);
// 3. Use useCompute like any other function!
const { data, loading, run } = useCompute<
{ data: number[]; width: number; height: number; passes: number },
number[]
>('blurImage');
const handleBlur = () => {
const canvas = canvasRef.current!;
const ctx = canvas.getContext('2d')!;
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
run({
data: Array.from(imageData.data),
width: canvas.width,
height: canvas.height,
passes: 100,
});
};
// Update canvas when result arrives
useEffect(() => {
if (data && canvasRef.current) {
const canvas = canvasRef.current;
const ctx = canvas.getContext('2d')!;
const imageData = new ImageData(
new Uint8ClampedArray(data),
canvas.width,
canvas.height
);
ctx.putImageData(imageData, 0, 0);
}
}, [data]);
return (
<div>
<canvas ref={canvasRef} width={256} height={256} />
<button onClick={handleBlur} disabled={loading}>
{loading ? 'Processing...' : 'Blur Image (WASM)'}
</button>
</div>
);
}Key benefits:
- WASM runs in a Web Worker via
useCompute: UI stays responsive - Same familiar
loading,data,errorpattern as other compute functions - WASM memory management encapsulated in the registered function
- Can easily add progress reporting, cancellation, etc.
kit.register('sum', (arr: number[]) => {
return arr.reduce((a, b) => a + b, 0);
});
const bigArray = Array.from({ length: 10_000_000 }, () => Math.random());
const sum = await kit.run('sum', bigArray);kit.register('grayscale', (imageData: Uint8ClampedArray) => {
const result = new Uint8ClampedArray(imageData.length);
for (let i = 0; i < imageData.length; i += 4) {
const avg = (imageData[i] + imageData[i + 1] + imageData[i + 2]) / 3;
result[i] = result[i + 1] = result[i + 2] = avg;
result[i + 3] = imageData[i + 3]; // Alpha
}
return result;
});kit.register('longTask', async (data, { reportProgress }) => {
const total = data.items.length;
const results = [];
for (let i = 0; i < total; i++) {
results.push(process(data.items[i]));
if (i % 100 === 0) {
reportProgress({ percent: (i / total) * 100 });
}
}
return results;
});
// React: track progress
const { progress, run } = useCompute('longTask', {
onProgress: (p) => console.log(`${p.percent}% complete`),
});Get autocomplete and type safety for your compute functions by extending the ComputeFunctionRegistry interface:
// Extend the registry (in a .d.ts file or at the top of your file)
declare module '@computekit/core' {
interface ComputeFunctionRegistry {
fibonacci: { input: number; output: number };
sum: { input: number[]; output: number };
}
}Now you get full type inference:
// β
Types are inferred - no need for generics!
kit.register('fibonacci', (n) => {
// n is inferred as number
if (n <= 1) return n;
let a = 0,
b = 1;
for (let i = 2; i <= n; i++) {
[a, b] = [b, a + b];
}
return b;
});
const result = await kit.run('fibonacci', 50); // result is number
// β TypeScript error: Argument of type 'string' is not assignable
await kit.run('fibonacci', 'not a number');Works with React hooks too:
// Types inferred from registry
const { data, run } = useCompute('fibonacci');
// data: number | null, run: (n: number) => voidSee the API Reference for more details.
Main class for managing compute operations.
const kit = new ComputeKit(options?: ComputeKitOptions);| Option | Type | Default | Description |
|---|---|---|---|
maxWorkers |
number |
navigator.hardwareConcurrency |
Max workers in the pool |
timeout |
number |
30000 |
Default timeout in ms |
debug |
boolean |
false |
Enable debug logging |
remoteDependencies |
string[] |
[] |
External scripts to load in workers |
Load external libraries inside your workers:
const kit = new ComputeKit({
remoteDependencies: [
'https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.21/lodash.min.js',
],
});
// Declare the global type for TypeScript support
declare const _: typeof import('lodash');
kit.register('processData', (data: number[]) => {
return _.chunk(data, 3);
});
β οΈ Important: Minification CompatibilityWhen using remote dependencies, use
declare constinstead ofimportto ensure compatibility with production minifiers (Vite, esbuild, etc.).// β Correct - works after minification declare const dayjs: typeof import('dayjs'); kit.register('format', (d) => dayjs(d).format()); // β Incorrect - breaks after minification import dayjs from 'dayjs'; kit.register('format', (d) => dayjs(d).format());This is because minifiers rename imported variables but preserve free variables declared with
declare const.
#### Methods
| Method | Description |
| ---------------------------- | ----------------------------- |
| `register(name, fn)` | Register a compute function |
| `run(name, input, options?)` | Execute a function |
| `getStats()` | Get pool statistics |
| `terminate()` | Cleanup and terminate workers |
### Compute Options
```typescript
await kit.run('myFunction', data, {
timeout: 5000, // Override default timeout
priority: 10, // Higher = runs first (0-10)
signal: abortController.signal, // Abort support
onProgress: (p) => {}, // Progress callback
});
Primary hook for running compute functions.
const {
data, // Result data
loading, // Boolean loading state
error, // Error if failed
progress, // Progress info
status, // 'idle' | 'running' | 'success' | 'error' | 'cancelled'
run, // Function to execute
reset, // Reset state
cancel, // Cancel current operation
} = useCompute<TInput, TOutput>(functionName, options?);
### `useComputeCallback`
Returns a memoized async function (similar to `useCallback`).
```typescript
const calculate = useComputeCallback('sum');
const result = await calculate([1, 2, 3, 4, 5]);
Monitor worker pool performance.
const stats = usePoolStats(1000); // Refresh every 1s
// stats.activeWorkers, stats.queueLength, stats.averageTaskDurationRegister and use a function in one hook.
const { run, data } = useComputeFunction('double', (n: number) => n * 2);ComputeKit supports WASM via AssemblyScript for maximum performance.
// compute/sum.ts
export function sum(arr: Int32Array): i32 {
let total: i32 = 0;
for (let i = 0; i < arr.length; i++) {
total += unchecked(arr[i]);
}
return total;
}npx asc compute/sum.ts -o compute/sum.wasm --optimizeimport { loadWasmModule } from '@computekit/core';
const wasmModule = await loadWasmModule('/compute/sum.wasm');
// Use with your compute functions-
Transfer large data : Use typed arrays (Uint8Array, Float64Array) for automatic transfer optimization
-
Batch small operations : Combine many small tasks into one larger task
-
Right-size your pool : More workers β better. Match to CPU cores.
-
Use WASM for math : AssemblyScript functions can be 10-100x faster for numeric work
// β Slow: Many small calls
for (const item of items) {
await kit.run('process', item);
}
// β
Fast: One batched call
await kit.run('processBatch', items);const kit = new ComputeKit({
workerPath: '/workers/compute-worker.js',
});For SharedArrayBuffer support, add these headers:
// vite.config.ts
export default {
server: {
headers: {
'Cross-Origin-Opener-Policy': 'same-origin',
'Cross-Origin-Embedder-Policy': 'require-corp',
},
},
};computekit/
βββ packages/
β βββ core/ # @computekit/core
β β βββ src/
β β β βββ index.ts # Main exports
β β β βββ pool.ts # Worker pool
β β β βββ wasm.ts # WASM utilities
β β β βββ types.ts # TypeScript types
β β βββ package.json
β β
β βββ react/ # @computekit/react
β βββ src/
β β βββ index.ts # React hooks
β βββ package.json
β
βββ compute/ # AssemblyScript functions
β βββ blur.ts
β βββ fibonacci.ts
β βββ mandelbrot.ts
β βββ matrix.ts
β βββ sum.ts
β
βββ examples/
β βββ react-demo/ # React example app
β
βββ docs/ # Documentation
This repository has an MCP (Model Context Protocol) server that lets AI assistants access the ComputeKit documentation and codebase directly. Use it as context in tools like VS Code Copilot, Cursor, Windsurf, Claude Desktop, and others.
MCP Server URL:
https://gitmcp.io/tapava/compute-kit
Add the following to your .vscode/mcp.json (create the file if it doesn't exist):
{
"servers": {
"computekit": {
"type": "sse",
"url": "https://gitmcp.io/tapava/compute-kit"
}
}
}Alternatively, you can add it to your User Settings (settings.json):
{
"mcp": {
"servers": {
"computekit": {
"type": "sse",
"url": "https://gitmcp.io/tapava/compute-kit"
}
}
}
}Go to Cursor Settings β MCP and add a new server:
{
"mcpServers": {
"computekit": {
"url": "https://gitmcp.io/tapava/compute-kit"
}
}
}Add to your ~/.codeium/windsurf/mcp_config.json:
{
"mcpServers": {
"computekit": {
"serverUrl": "https://gitmcp.io/tapava/compute-kit"
}
}
}Add to your Claude Desktop config (claude_desktop_config.json):
{
"mcpServers": {
"computekit": {
"command": "npx",
"args": [
"-y",
"@anthropic-ai/mcp-proxy@latest",
"https://gitmcp.io/tapava/compute-kit"
]
}
}
}Config file location:
- macOS:
~/Library/Application Support/Claude/claude_desktop_config.json- Windows:
%APPDATA%\Claude\claude_desktop_config.json
Contributions are welcome! Please read our Contributing Guide first.
# Clone the repo
git clone https://github.com/tapava/compute-kit.git
cd compute-kit
# Install dependencies
npm install
# Build all packages
npm run build
# Run React demo
npm run dev
# Run tests
npm testMIT Β© Ghassen Lassoued
Built with β€οΈ for the web platform