Hi All,
For some time been working on a Racket to WebAssembly compiler.
Today the website went live:
https://soegaard.github.io/webracket/
/Jens Axel
Hi All,
For some time been working on a Racket to WebAssembly compiler.
Today the website went live:
https://soegaard.github.io/webracket/
/Jens Axel
Feature request for the space invaders demo:
Back in my days, there could be only one bullet in the screen (due to a sprite number constrain?). So missing a shoot was painful because I had to wait until the bullet reached the top of the screen. In this version I can just press space to shoot, and it removes part of the anxiety shooting the few last invaders.
I'll add a hard mode!
Not necessary for a code demo, but I agree this is an essential aspect of the space invaders "experience!"
https://soegaard.github.io/webracket/new/space-invaders.html
I have added a new hard mode. It's tricky to calibrate the difficulty.
Let me know if it is too hard.
Hi All,
From the feedback yesterday the main points were:
Ad 1. I rewrote the compiler overview. It is no longer short. https://soegaard.github.io/webracket/documentation-compiler-overview.html
Ad 2. I have attempted to make an alternative design. Use the new/old button in the uper right corner to switch between them. Far from all pages have been polished for the new design. However, check out the same compiler overview in the new design: https://soegaard.github.io/webracket/new/documentation-compiler-overview.html Also, check also the front page.
Ad 3. I have rewritten the worst cases of AI inspired wording. However, I need help to improve the text.
What's the verdict - is the new design worth pursuing or should I stick to the dark one?
I can't find the licence of the project. Are you planing to add one later?
Did you forget to commit space-invaders-rkt? I disagree with the bullet behavior in hard mode, and I was thinking about sending a PR instead of explaining the difference. (I can write the difference if you prefer.)
Wonderful. I installed it, ran the examples locally .. but now I feel dumb because I canât get my own program to run. And the Quick Start doesnât have a section that says âhere is how you run your own Racket programs in the browser.â Could that be added next for people like me? Thanks.
Things are a little messy now, I am experimenting with a new website design.
Space Invaders on the dark website is here:
To build it, to go web-site/src and use ./build.sh this will the dark web site.
Then go to web-site/local/ this folder holds the website.
Run raco static-web and open the web-site in the browser.
But - since this build the entire web-site including all examples building is a little slow.
The alternative is to change examples/space-invaders /space-invaders.rkt which is a standalone example.
But I think the latests change haven't been made here - so you might need to copy from the web-site version. That folder has build instructions in the readme.
PS: I would love to receive my first PR.
/Jens Axel
And the Quick Start doesnât have a section that says âhere is how you run your own Racket programs in the browser.â Could that be added next for people like me?
Working on it!
I made 3 hello world examples today, but haven't had time to put them in the Quick Start yet.
All examples should have build instructions in the readme.
Most also in the source itself.
The first one, hello-world-1, shows that
racket webracket.rkt -r
will build the program and run it in terminal (via Node).
This is useful for automatic testing of non-web related programs.
The second one hello-world-2 shows how to use --browser to build for the browser.
It generates both an wasm bytecode file and an html file that will load the webassembly and run it. This version uses the dom API to build nodes to display.
Due to security in modern browser, one can't just open the hml file in a browser.
You need to start a local web-server that serves the file.
So, one can use raco static-web in, say, the root of the repo.
The third one, hello-world-3, uses sxml to generate the nodes.
Finally, I should mention that one can use #lang webracket in DrRacket to test non-web programs. This language doesn't invoke the WebRacket compiler, but rather provides the primitives and forms that WebRacket currently supports. The idea is two-fold. First, users can develop WebRacket programs with a fast compiler. Second, I can use it to test whether the output of my compiler is correct.
/Jens Axel
UPDATE - Added paragraph on why raco static-web is needed.
I forgot to include this:
soegaard@mbp3 webracket % racket webracket.rkt --help
usage: webracket [ <option> ... ] <filename>
<option> is one of
-r, --run
Run the program after compilation.
-v, --verbose
Compile with verbose messages
--wat-file <filename>
Filename for the wat file
--wasm-file <filename>
Filename for the wasm file
--host-file <filename>
Filename for the host file
--label-map-forms
Include (form ...) entries in .wasm.map.sexp
--no-label-map-forms
Omit (form ...) entries in .wasm.map.sexp
--timings
Print timing breakdown for compilation steps
--stdlib
Include the standard library
/ -b, --browser
| Generate code for browser.
| -n, --node
\ Generate code for Node.js
* -l <lf>, --link-flags <lf>
Add a flag <lf> for the linker
* --ffi <ffi-file>
Add .ffi file
--help, -h
Show this help
--
Do not treat any remaining argument as a switch (at this level)
* Asterisks indicate options allowed multiple times.
/|\ Brackets indicate mutually exclusive options.
Multiple single-letter switches can be combined after
one `-`. For example, `-h-` is the same as `-h --`.
[matthias @ CedarPark webracket (main)]$ racket webracket.rkt -r ../test.rkt
compiler.rkt:52:9: collection not found
for module path: nanopass/base
collection: "nanopass"
in collection directories:
/Users/matthias/Hub/SwDev/Lib/
/Users/matthias/0Unison/collects/
/Users/matthias/Library/Racket/development/collects
/Users/matthias/plt/racket/collects/
... [254 additional linked and package directories]
location...:
compiler.rkt:52:9
So I installed nanopass. Please do include with installation instructions.
[matthias @ CedarPark webracket (main)]$ racket webracket.rkt -r ../test.rkt
standard-module-name-resolver: collection not found
for module path: webracket
collection: "webracket"
in collection directories:
/Users/matthias/Hub/SwDev/Lib/
/Users/matthias/0Unison/collects/
/Users/matthias/Library/Racket/development/collects
/Users/matthias/plt/racket/collects/
... [255 additional linked and package directories]
context...:
/Users/matthias/Wasm/webracket/expander.rkt:16:0: topexpand
/Users/matthias/Wasm/webracket/compiler.rkt:5984:6
/Users/matthias/Wasm/webracket/compiler.rkt:5968:0: comp
/Users/matthias/Wasm/webracket/driver.rkt:49:0: drive-compilation
body of "/Users/matthias/Wasm/webracket/webracket.rkt"
Ah, I need to install it as a package. Done.
cat ../test.rkt
;; #lang racket
(define (hello w) (display 'world))
(hello 'Jens)
Could the compiler error messages be less Node-y?
[matthias @ CedarPark webracket (main)]$ racket webracket.rkt -r ../test.rkt
(WARNING unbound? display)
/bin/sh: wasm-tools: command not found
* Compilation Error *
node:internal/fs/promises:642
return new FileHandle(await PromisePrototypeThen(
^
Error: ENOENT: no such file or directory, open '../test.wasm'
at async open (node:internal/fs/promises:642:25)
at async Object.readFile (node:internal/fs/promises:1279:14)
at async file:///Users/matthias/Wasm/test.js:4:20 {
errno: -2,
code: 'ENOENT',
syscall: 'open',
path: '../test.wasm'
}
OK, display is no good. How about without:
[matthias @ CedarPark webracket (main)]$ racket webracket.rkt -r ../test.rkt
/bin/sh: wasm-tools: command not found
* Compilation Error *
node:internal/fs/promises:642
return new FileHandle(await PromisePrototypeThen(
^
Error: ENOENT: no such file or directory, open '../test.wasm'
at async open (node:internal/fs/promises:642:25)
at async Object.readFile (node:internal/fs/promises:1279:14)
at async file:///Users/matthias/Wasm/test.js:4:20 {
errno: -2,
code: 'ENOENT',
syscall: 'open',
path: '../test.wasm'
}
Node.js v24.11.0
At this point I am not sure how to proceed. But hey, this is going somewhere and Iâd love to see progress.
Do you want actual PRs for these?
The overall lesson is that I need to make a webracket package in order to make this easier to install. Maybe make raco webracket invoke webracket.rkt to make it easier to run.
Anyways, for now I'll
nanopasswebracket as a packageThen comes the:
Could the compiler error messages be less Node-y?
>
/bin/sh: wasm-tools: command not found
* Compilation Error *
node:internal/fs/promises:642
What happened here is that wasm-tools is missing from your path.
Thus the wat (text format of WebAssembly) was never compiled to wasm (bytecode).
The compiler driver should have stopped here - but it continued and made node run the non-existing test.wasm file. And what you see the error from Node.
I'll improve the compiler driver to check that wasm-tools is in the path.
Lastly, in order to use display you need to add --stdlib as an option.
This is not obvious. The standard library contains the part that are implemented in WebRacket. This includes reading and writing.
Now that the compiler is somewhat stable, I need to flip it.
The standard library ought to be be included as default.
Then a switch --no-stdlib could omit it.
Finally, I need to check why you didn't get a normal "identifier unbound" error,
for display when the switch --stdlib is not used. That was the expected error message.
You probably already installed wasm-tools, but if not:
https://soegaard.github.io/webracket/installation.html
/Jens Axel
Thanks for the reminder!
I have added a license (MIT).
I had placed wasm-tools on my path or so I thought .. wrong.
When I did place it on my path, I got the usual Apple warning about malware and such.
Your instructions would probably benefit from a "sudo xattr -d com.apple.quarantine /usr/local/bin/wasm-toolsâ
Now that it runs, I get this:
[matthias @ CedarPark webracket (main)]$ racket webracket.rkt -r ../test.rkt
world
Okay not bad .. but I really want this to show in the browser ⌠which I take it comes with -b.
All said and done, I think your docs need a write-up on how to run WebRacket programs in the browser.
But hey, this is huge progress. Thanks.
Great to hear -r now works. Getting something in the browser should now be easy.
Okay not bad .. but I really want this to show in the browser ⌠which I take it comes with
-b.
This is where the two examples hello-world-2 and hello-world-3 enters the picture.
Let's look at the last one:
;; Build a dom node with a small hello message.
(define content
(sxml->dom
'(div
(h1 "Hello!")
(p "This page was compiled from Racket to WebAssembly."))))
;; Get the (currently empty) body node and append our message.
(define body (js-document-body))
(js-append-child! body content)
;; Use the JavaScript Console for debugging.
(js-log "Hello!")
Inside the hello-world-3 folder:
racket ../../webracket.rkt --stdlib --ffi dom -b hello-world-3.rkt
Then start a local web server:
raco static-web
Go to the address printed by static-web.
All said and done, I think your docs need a write-up on how to run WebRacket programs in the browser.
Agreed!
@EmEf
Thanks for the feedback - you must be the first besides me to give WebRacket a spin.
Based on the discussion, I have made some improvements:
The installation instructions for wasm-tools now include:
I believe using "Privacy and Security" ought to be enough, but the command is a fine alternative.
Instructions for installing nanopass were added.
Instructions for installing webracket as a package was added.
The compiler driver was improved.
It now does preflight checks:
wasm-tools and node are in the pathwasm-tools parse is workingnode understands the needed flagsAlso, it now checks that output files actually are produced by the external tools as expected.
One more thing.
Now that the compiler is somewhat stable, I need to flip it.
The standard library ought to be be included as default.
Then a switch--no-stdlibcould omit it.
I decided to do the flip. The standard library in stdlib/ is now automatically included.
This means it is no longer needed to use --stdlib when compiling WebRacket programs.
Some test programs now need --no-stdlib but that's irrelevant to users.
Note: The downside of including the standard library is increased the compile times.
The flag --no-stdlib can thus be used to opt put. Since the standard library contains display one can instead use the primitive js-log to write to the JavaScript console.
On Feb 25, 2026, at 8:24 AM, Jens Axel Søgaard wrote âthanks for the feedback - you must be the first besides me to give WebRacket a spin.â â
Now we need six more people to do so to reach the magic level of dissemination ![]()
I tried to get the following small modification to run:
(define (hello w)
(define a (sxml->dom (hello/html w)))
a)
(define (hello/html w)
`(div (h2 "hello")
(p (symbol->string w))))
;; I had initially included the following in `hello`
(define body (js-document-body))
(define more (hello 'Jens))
(js-append-child! body more)
I compiled it, and this succeeded w/o a problem. Then I ran raco static-web and opened localhost at the specified port.
-- I do get my "test.html" file but when I open it in "localhost", I don't see anything. It's a blank page.
;; - - -
I pulled your repo and ran race-setup on it. I noticed two major problems:
It complles too many things, especially WebRacket files. Perhaps you should consider a different file extension (.wrkt?) and then exclude those in the "info.rkt" file when you make a package.
The name js-eval shows up as undefined. My guess is that this is needed somewhere in my example but I was reluctant to read a 120k file. Question: Am i missing one of your packages that this relies on?
FWIW I did get "examples-3" to run and display the desired output.