module Hello exposing (main)
main : String
main =
"👋"
$ run make -o hello CLI/Hello.elm
$ ./hello
"👋"
“
” friends!
It’s finally here: a true native Elm experience we’ve all wished for! elm-run will run your Elm on the server and on your CLI, in your TUI programs and in your games, unspoiled! No more of that ugly JavaScript duck tape we had to do so far.
After exactly six months of nonstop development, behold the first beta version!
What is it exactly?
It’s a runtime the same way Node.js is for JavaScript code: you compile your Elm to assembly code and it either executes it directly or produces true native OS binaries, like a C compiler would. Runs on macOS, Linux, and Windows.
┌───────────┐
│ │─┐
│elm source │ │┐ ┌─────────────┐ ┌─────────────┐ ┌────────────┐ ┌────────────┐
│ │ ││ │ │ │ │ │ │ │ │
└───────────┘ │├─────►│elm compiler ├────►│ elm ir ├──────►│ js codegen ├────►│ browser │
└────────────┘│ │ │ │ │ │ │ │ │
└───────────┘ └─────────────┘ └──────┬──────┘ └────────────┘ └────────────┘
│
▼
┌─────────────┐ ┌────────────┐ ┌────────────┐
│ │ │ │ │ │
│run optimizer├──────►│ codegen ├────►│ runtime/OS │
│ │ │ │ │ │
└─────────────┘ └────────────┘ └────────────┘
In short, you use the exact same Elm you know and love, and elm-run runs it for you on your computer + server, fast, right on the OS, just like cat or grep or other utilities. It runs all existing published packages. (Okay, maybe not those made for the browser).
But it also runs Elm in a fast, native 3D game engine: from your Elm program straight to the GPU!
No weird transpilations, “bindings,” and whatnot! No contorted “diagram languages.” Just Elm the way you already write it.
elm-run is not an experiment: it’s a proper product aimed at professionals. And for enthusiasts, with elm-game. Everyone benefits, but the features are chosen for pro use, informed by my professional experience (SaaS, e-commerce, e-mail, databases).
Is it really native?
Yes! The real thing! No piggybacking on Node, no “… well, actually it’s just lipstick on JavaScript.”. No transpiling to some other source for yet some other compiler to process, no llvm. (Believe it or not, llvm produces 15% slower VM run loop than the one we ship! Seriously). So, it’s just Elm and your computer.
No, it’s not WebAssembly. elm-run does its own VM + codegen + native emission. elm-run is written in itself, without dependencies: it compiles Elm intermediate language, optimizes for native, emits assembly code for macOS, Linux, and Windows with Intel and Arm processors. It’s not a “dynamic language.” It’s ahead-of-time compiled and optimized bytecode. It also compiles down to straightline executable machine code!
It’s open source, like Elm, and it’s not some “language inspired by Elm:” it runs the stock Elm compiler, unchanged and will never ever attempt to change Elm.
Why?
I really enjoy writing programs in Elm. It’s a fun and pleasant language. In my business, I have needs beyond the browser, and jumping between the pleasant Elm experience and other languages suitable for backend/db development caused me unpleasant cognitive dissonance for years. And I just can’t swallow all this JS/node business. Never could.
Add to that my growing concern with all these security issues we’re seeing with the supply chain, a chance idea (I needed an interpreter for elm-wrap), and well, here we are: “
”.
A proper solution deserves dedication and focus, so I decided to invest this year in solving my cognitive dissonance and lay the groundwork for reimagining the way we write, run, and share code. Put my business aside, and dove into this for days and days, without pause.
The outcome is elm-run, the runtime. Behold! An unapologetically ambitious product that aims to elevate Elm in your life and your work!
Use it today
Today I’m inviting you to beta test:
- elm-run compiler, the thing that takes Elm compiler’s output and produces the bytecode / native outputs.
runorchestrator tool that lets you manage your native Elm coding experience.
The runtimes (things that execute those compiled programs):
- Command line
host-runthat runs CLI programs and TUI programs; host-worker, a web server with integrated async/multithreaded Elm runtimes, automatic TLS, etc.;- and elm-game, the 2D/3D game engine.
My new favorite!
I’ve been enjoying all these things for a while now: writing code generators for the virtual machine and standard library, CLI utilities, profiler, debugger, etc. It’s fun! It even runs on elm-wrap cloud in production! However modest that may be, we can say it’s already a “production runtime,” even though we’ve still got a ways to go before a 1.0 release.
What do these do?
Cli + TUI
The CLI runtime is the workhorse, the daily driver.
So far we had to jump through hoops with various JS runtimes, have Ports blues, and spend most of our time writing this horrible plumbing instead of being productive with Elm!
elm-run changes that forever!
Write your normal headless Elm like you are used to, run it and that’s it! It will run both simple and complex workloads, async or multi-threaded (Actors! Yay!), fetch stuff with HTTP, TCP, or sockets. It knows all the advanced things one would expect from a proper “server-grade runtime.” (protip: Nonchalantly throw around terms like shared memory at team meetings).
CLI runtime is powerful and fast. It’s tighly integrated with Elm: it knows Elm in the async reactor core! It’s made for Elm and for TEA and nothing else! No compromise! run utility runs on that one, so does the compiler. It also works as a proper OS process on the server, like your job workers. In fact, it’s great on the server! With the bytecode distribution model, it’s way safer than shipping just some random llvm-produced executables. It has all the goodies that a pro ops team
would expect: SFI, auditing, capabilities, …
Programming is exactly how you’d imagine. It has the run loop and init->update->subscriptions cycle. It can do one-shot batch mode or run infinitely as an OS process.
Here’s the grep example from The Book of elm-run (that you’ll get in the beta toolkit):
It also knows how to render TUIs for you. Natively integrated into your OS, with a fast view engine. Just like all those other TUI toolkits. Except this one feels like home:
import Terminal.Tui.View as V
...
bg : ( Int, Int, Int ) -> V.Attribute msg
bg ( r, g, b ) =
V.bgRgb r g b
view : Model -> View Msg
view model =
let
cols =
max 1 model.windowSize.cols
rows =
max 1 model.windowSize.rows
bodyRows =
max 1 (rows - 2)
in
V.column
[ V.widthCells cols
, V.heightCells rows
, bg Theme.bg
, fg Theme.text
]
[ topBar cols model
, dashboard cols bodyRows model
, footerStatus cols model
]
main : Terminal.Program Model Msg
main =
Terminal.program
{ init = init
, view = view
, update = update
, subscriptions = subscriptions
}
I find it practical to have a small TUI app around to monitor the production db of my wife’s online shop. But I also use little TUI programs for overseeing tests, matrix tests, and a bunch of other things like analyzing code the compiler produced.
Actors
All hosts run a fully async reactor loop (written in C targetting native async idioms of each OS). Parallelism is through Actors that are nothing more than “mini-TEA” programs themselves:
Actor.spawn
(Actor.program
{ init = workerInit
, update = workerUpdate
, subscriptions = workerSubscriptions
}
{ replyTo = mainPid }
)
Simple and fun! Familiar and powerful! Super useful for non-blocking UIs in TUI and elm-game.
There are also simpler “Type A” actors that are just Tasks executed on their own thread.
Web server
A web server that serves async web requests exactly how you’d imagine: subscribe to a request and process it async! Or maybe you want to process a web socket or a TCP connection. Familiar and boring in a good way!
This is my test corpus fixture, httpbin style:
main : Worker.Program Model Msg
main =
Worker.program
{ init = init
, update = update
, subscriptions = subscriptions
}
subscriptions : Model -> Sub Msg
subscriptions model =
Http.onRequest GotRequest
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
case msg of
GotRequest req ->
if req.path == "/quit" then
( model, Worker.exit 0 )
else if req.path == "/echo" then
-- Echo the request body verbatim. Used by HttpPostBodyTest /
-- HttpPutBodyTest to verify the client forwarded the body.
( model
, Http.respond
{ connectionId = req.connectionId
, streamId = req.streamId
, status = 200
, contentType = "text/plain"
, body = req.method ++ " " ++ String.fromInt (String.length req.body) ++ " " ++ req.body
}
)
...
It runs the same VM as the other hosts, so it can do all the same things like spawn actors, OS processes, etc. It also happens to be the industrial-strength, hyperscaler-grade web server.
The Game
And then there’s elm-game, my new favorite! Hadn’t looked at it for months, thinking I’ll release by 0.6.0 per the roadmap. But then @MartinS asked if it can mix audio in pure Elm and stream it. Hmm…
So yes, pure audio mixing in Elm and just stream it to the OS: Sound.queuePcm { sampleRate = outputSampleRate, channels = 2, samples = samples } . Look ma! No ports!
Haven’t done anything but since Friday, almost missed the release deadline today! Disaster! But haven’t had this much fun in a while!
Always been a fan of Lua LÖve, even tried making a few small games with my son way back when. I’d patiently explain to him, “Son, every engine always has an init phase, the run loop, and the frame renderer. And it processes your stick and keyboard inputs when they come. Nothing much to it: init, update, view, pick up input events.”
Time passed, he studies Game Design. Early on into elm-run, he’s working next to me in the office, struggling with his assignment in Unreal. That beast might be the “cutting edge”, “pro” thing, but wow does it kill any joy! You have an idea for a quickie little mechanic to try out, but that beast just hammers you in with all the endless menus and weird UI stuff and its C++ that isn’t! You’re like, never mind.
Hmm,
… init, view, update, subscribe… native Elm on my screen…
It’s a time stealing monster friends! It’s so addictive, I’m afraid to release it before we test the “pro” runtimes! I’m afraid you’ll ignore the cool web server and the CLI! (I keep convincing myself, “it’s good for perf testing.”)
It’s a decent little engine, in the spirit of Elm. Does all the usual 2D and 3D stuff; and audio without Ports! Obviously, multithreaded through Actors. And it’s just Elm, packages and all. Not being much of a game maker, I’m hoping all you good people will make it great! And make great games! Elm game jams! Yay!!!
It’s a really funny feeling to write Markdown in immediate mode in Elm, just blitting pixels to the screen (and the GPU) and laying things out. Didn’t expect it to feel so liberating without Html and virtual dom!
Welcome to Native!
So friends, we start today! This early beta is by invitation. Not because I don’t love you (I do,
). It’s just that I have limited mental bandwidth, and have been writing this code for so long that I simply don’t hit the bad corners anymore. Yet almost everyone that tried it ran into a new problem I haven’t seen before within hours: either under-optimized parts or mysterious compile errors due to code shapes I never thought of trying, or stacks of packages I didn’t know existed!
Native here truly means that: three platforms, two architectures. All the stuff that V8 and the browser do for Elm in the Browser, elm-run has to do by itself. And there’s a LOT of it! No single human can hope to test that and find all the rough spots.
So, if you don’t mind, I’d like you to tell me you’re interested at the beta page by sending some info about your hardware, and then I’ll start contacting you, subject to how quickly I can turn around any issues we find, and your declared tolerance for things that break.
The goal is to onboard everyone rapido! I’d love to start with a good mix of macOS/Linux/Windows early adopters, and then expand in order of improvements and my ability to support everyone.
I’m more of a relationship person, preferring to work one-on-one with interested people. If you’re trying it, means you invested your time into something I did, so at least I can pay you back with attention and support.
The smoother things get, the more people I can onboard. I gather a month, maybe two, should do until we start seeing really stable products. And hopefully some games!
This is the first time for me to release such a massive and ambitious developer product. I know for a fact that for most of you it is a waste of time have to try something only for it not to meet your expectations, and then you have to wait for things to improve. Kills the fun. I’d rather you have a great start. Some people, on the other hand, are tolerant to these things. So I’m hoping we have a good mix here!
I’m very much looking forward to Elm Camp to have productive and informed discussions and talk ideas! I’m DYING to see the stuff you’ll do.
The Conversation
I’ll provide communication details in the invitation (issue reporting, binary updates, etc). elm-talk will likely be the only channel for bug reporting and technical discussions. It’s almost ready to go live! Just a bit more backend work.
In the meantime, please ask questions here in public, or contact me through this discourse, on Slack, Discord, phone, whatever works for you! My door is always open and I’m excited to talk!
I’ll post occasional updates here and on the site as well.
Mandatory Answers
-
Is this open source?
Yes, bona-fide, BSD licensed open source. Just like Elm itself.
Even comes with a book of internals, so you can learn stuff if you’re into low level things. But the source for the runtime and compiler is not yet public. It will be, but only after 1.0. -
What’s it made in? Did you use hip language/tool X?
No.
I started on December 1 '25 by writing a small C interpreter for Elm IR, slowly getting it just fast enough to run larger Elm programs, and then moved as much as I could to Elm.
A career in tech taught me that the best and most sustainable products come from making your own cake all the way. No transpiling to X, no llvm backend, no some thing random people on the internet tell you is magically fast because is written in some equally magical language that some other random people say that is fast because, well, some rando in some big brand company tried it once and tweeted about it.
Friend, this cake has only three ingredients: Elm, assembly (written in Elm), C. More and more Elm/assembly, less and less C. -
Yes. It builds on Linux, macOS and Windows.
macOS is my platform so that’s the most tested. I wrote most things cross-platform and am now building on all platforms daily. And Elm bytecode tools obviously run cross-platform. One nice benefit of choosing that architecture. Saves a lot of build minutes! -
Is it fast? Yes, decent. But let’s revisit this question after v0.5.0.
The VM already executes around the same speed as WASM3 interpreter on a few synthetic WASM tests I could find. That target worked as predicted! Not very meaningful for judging the speed of your programs, but at least the base is ok. The Book guides you in writing a small CLI program that goes faster than system utilities likegrep.
There’s also a very very fast compiler to native executables that runs more and more programs on the test corpus. I don’t know the final speed yet, but writing all that assembly is paying off! Let’s wait for 0.5.0. -
Wut? A “VM”?! That’s slow! Cool kids use llvm!
A purpose-built runtime with tight loops and ops in handwritten assembly is the right choice for all the productive scenarios Elm is suitable for. You can’t do secure distribution with native binaries without cooperation of OS gatekeepers. Ask any pro ops person: small, audited runtime with SFI and capabilities + inspectable deliverables; or an opaque executable compiled with some rando’s llm’d llvm that won’t be buildable with the next version of llvm?
elm-run aims at pro use. It’s fast enough. And as mentioned, llvm/mlir/whatevaIR are not made for functional languages. No matter what they sell you. A runtime and compiler that know the language intimately, made for that language–in that language–are an infinitely larger long-term win over chasing “native compilation” by depending on some third party’s vision of what that means.
If I may say so myself, elm-run is an elegant and enjoyable product–second only to Elm itself!-- so you won’t mind waiting a few more months for insta-compiled native. (And you won’t have to recompile whatever you already shipped). -
Can I run it in production?!
I do! It’s still beta, though, and it is beta exactly so you run it in prod and help discover any warts! But I don’t know what you do. Let’s talk. -
Is this it?
No. This is just table stakes to do all the exciting stuff. I’m looking forward to your help; the minute this product is out, I have an insane pipeline of stuff to release. Elm is truly a productivity language! And now unchained from the browser,… wow!
Once you pick this up, I hope you’ll be as excited as I am!
In a most embarrassing turn of fate, I misconfigured a backend and the beta page can’t submit applications! Super embarrassing! Blame elm-game! Will provision a new server soon with a clean setup, but safer to say that the page goes up tomorrow. Contact me directly in the meantime! Don’t hesitate.
The form now works if that’s your preferred way. Thank you all for great interest! ![]()




