Over the last few months, I’ve been working on something I’ve always wished JavaScript had by default: deterministic concurrency.
In most cases, JS async behavior depends on the event loop, Promise.race, or microtasks. That means timing, order, and scheduling aren’t truly predictable - even if your code looks flawless. Small differences can change execution order in ways that are hard to trace.
Pulse is a small experimental language that compiles to JavaScript (ES Modules) but runs on its own deterministic runtime. Under the hood, it’s reactive like Solid or Svelte, but it also brings in Go-style concurrency as a first-class concept.
Why I built Pulse
I spend most of my time switching between JavaScript and Python. I wanted something that felt as expressive as JS, but with concurrency I could actually rely on - no hidden race conditions or random task order.
When I say deterministic, I mean that the same program should behave the same way every time it runs. No invisible timing gaps, no unpredictable behavior.
Pulse started as a small experiment to explore what a fully predictable async model could look like inside the JS ecosystem. Over time, it grew into a full compiler, runtime, and standard library.
What’s new in 1.0.2
This release makes the runtime stable enough for real-world async workflows.
- Deterministic scheduler (no Promise.race, no setTimeout)
- for await ... of channel now works - channels are async iterable
- spawn keyword for lightweight concurrent tasks
- select {} statement with stable, source-order priority
- Parser now supports optional semicolons
- All examples from the docs compile and run
- Fully reproducible npm package
You can install it now:
npm install pulselang
How it works (simplified)
A channel in Pulse works like a tiny pipe between concurrent tasks.
When one side sends a message, the other side receives it in exact order. No event-loop juggling, no task stealing, no race conditions.
A scheduler runs all tasks in a cooperative round-robin queue - it decides precisely who runs next and when. That’s what ensures deterministic behavior: every run produces the same result.
Pulse doesn’t rely on any of JavaScript’s non-deterministic primitives.
No microtask queue, no random scheduling - everything is explicit and predictable.
Example
// simple concurrent pipeline
spawn {
for await (x of numbers) {
out.send(x * 2)
}
}
spawn {
for await (y of out) {
log("got", y)
}
}
select {
case msg <- in: handle(msg)
default: sleep(10)
}
All of this compiles down to plain ES Modules, so you can use Pulse with:
- Node.js (v18+)
- Next.js / React
- Vue or SvelteKit (via build scripts)
Determinism test rig
Before publishing version 1.0.2, I ran full fuzz and soak tests:
- 100/100 identical runs across buffered, unbuffered, and select scenarios
- /400 fuzz cases passed (FIFO ordering, select determinism, no lost wakeups)
- 19.4M runs at ~64,800 runs/sec - no memory leaks
- No use of Promise.race, setTimeout, or setImmediate anywhere
Every .tgz package built from npm is bit-reproducible (same SHA-256 hash).
Try it
Docs & examples: https://osvfelices.github.io/pulse
Code: https://github.com/osvfelices/pulse
I’d love to hear what you think - especially if you enjoy runtimes, compiler design, or async patterns.
And if you can break it… even better. That’s how good runtimes evolve.
Top comments (0)