Remix.run Logo
Theseus: Translating Win32 to WASM(neugierig.org)
50 points by ingve 3 days ago | 22 comments
kettlecorn 2 hours ago | parent | next [-]

I am unfamiliar with this project and only skimmed this post but if this uses Rust for the main binary blob it should be possible to have the main thread blob shared with the other threads even with the blocking.

The blog post cites the concern that malloc could block, however when Rust's standard library is compiled with support for atomics enabled the Rust allocator's locking implementation busy loops instead of waiting on the main thread.

See the comment in the Rust source here: https://github.com/rust-lang/rust/blob/77a4fb62f70c6ea05e182...

This means that if care is taken to avoid any other code that makes the main thread wait it should be possible to use a single shared binary instead of the more convoluted approach presented in the blog post.

AgentME 35 minutes ago | parent | next [-]

People in the WebAssembly standards group are recently considering relaxing the limitation that the main thread can't use atomic.wait: https://github.com/WebAssembly/threads/issues/177#issuecomme...

evmar 10 minutes ago | parent [-]

Wow, thanks for the link, that is perfect timing! I submitted my post and my own feedback on the discussion: https://github.com/WebAssembly/shared-everything-threads/dis...

evmar 2 hours ago | parent | prev [-]

Gosh, I think that means even when your code is wholly running on workers (where you would be able to use the atomic wait mentioned in the comment), it still will busy loop, doesn't it? At least it's within the allocator and not in the general implementation of Mutex... I think?

hardwaresofton 3 hours ago | parent | prev | next [-]

If you’re poking around WASM consider falling down the Component Model rabbit hole:

https://github.com/WebAssembly/component-model/blob/main/des...

Threading is actively being worked on right now (to be released in 0.3.x, soon), and some changes just made their way into LLVM as well:

https://github.com/llvm/llvm-project/pull/175800

valorzard 2 hours ago | parent [-]

Iirc the async stuff in the component model isn’t actual threads but rather green threads, but correct me if I’m wrong

lukax an hour ago | parent | prev | next [-]

There is also Retrotick.

https://retrotick.com/

It simulates x86 (win32 and win16) and implements Windows APIs in javascript and renders window frames with DOM and contents with canvas (e.g. GDI translates to browser canvas operations). A lot of programs run already but a lot of APIs are not yet implemented.

I successfullt spent a few days extending it to run a Click & Create based game from my childhood.

dismas an hour ago | parent [-]

The author actually talks about this in an earlier post[0]

[0]: https://neugierig.org/software/blog/2026/04/theseus.html

modeless 2 hours ago | parent | prev | next [-]

Seems like Theseus is not using OffscreenCanvas yet. Should be a good fit for something that runs off the main thread. Graphics can be rendered from a background worker thread directly to the screen without the involvement of the browser main thread at all.

https://developer.mozilla.org/en-US/docs/Web/API/OffscreenCa...

evmar an hour ago | parent [-]

[post author] I looked into this API but wasn't sure how to make good use of it. I may have misunderstood, maybe you could help!

The doc you linked has two forms of use, sync and async.

For sync: it seems the idea is for the worker to render into an OffscreenCanvas, then postMessage an ImageBitmap created with transferToImageBitmap from worker to main thread for drawing. It seems like it would need to allocate a new bitmap for each frame. Currently Theseus puts the pixel data in shared memory and the main thread copies it out (required to create an ImageData), which at least in principle could reuse the copy buffer (though it currently doesn't), which seems better? https://github.com/evmar/theseus/blob/a5a849dbcf8046a2d1837a...

For async: in this the idea is have the worker render into an OffscreenCanvas linked to the on-screen one. But it seems to get an OffscreenCanvas in a worker, the main thread canvas must .transferControlToOffscreen() it to the worker. Under the current synchronization model[1] the only time the worker can receive a message is during startup, because the rest of the time it's deep in its own wasm call stacks. This means that if the worker needs to resize its canvas and then paint to it, it's stuck.

[1] I wrote "current" because after writing this post I learned about JSPI which might help with this.

modeless an hour ago | parent [-]

I think after you call transferControlToOffscreen() and transfer the OffscreenCanvas to the worker, then the worker can control the size of the canvas with the width and height properties of the OffscreenCanvas. I'm not sure how this interacts with layout though.

IIRC the transferToImageBitmap path is efficient and doesn't necessarily copy anything. The APIs are designed to allow the ImageBitmap you get to be a reference to the GPU texture that is the backbuffer of the canvas, not a copy. When you transfer it to the main thread you are supposed to draw it using an ImageBitmapRenderingContext, which doesn't need to do an extra blit. It's just directly composited with the rest of the page, all staying on the GPU. In theory if it's a full screen canvas with no DOM on top the browser could even skip compositing entirely though I don't know if that's implemented anywhere.

Of course there are probably a lot of ways to fall off the fast path. And I guess you are doing software rendering for GDI so you're not starting out with pixels on the GPU in the first place. I'm not sure what the best path is for you but I think you can probably benefit from OffscreenCanvas in some way.

mickael-kerjean 4 hours ago | parent | prev | next [-]

It's refreshing to see those kind of projects coming back on HN. Pretty awesome to see your example (https://evmar.github.io/theseus/) fit in about 1MB when a lot of those wasm projects can be super heavy (like libreoffice which is 50MB after brotli compression)

Findecanor 3 hours ago | parent | prev | next [-]

When I clicked, I thought this was going to be about the Theseus operating system [0]. Running win32 programs on it would have been ... interesting.

It too uses WASM, but for running non-Rust programs in sandboxes. Everything else is Rust. Hmm.. Last updated in 2024 though.

0: https://github.com/theseus-os/Theseus

univrsaltrans 2 hours ago | parent | prev | next [-]

Interesting project!

It's just a matter of time before we'll have a universal executable/binary translator that can run any program from any OS. These are artificial constructs when you think about it.

idle_zealot 2 hours ago | parent [-]

> It's just a matter of time before we'll have a universal executable/binary translator that can run any program from any OS

People have been working on these layers for a long time. It's understood that programs are almost always fundamentally portable and only unable to run due to peculiarities. Smoothing over those differences in format, whether they be at the machine code layer, API layer, whatever, is doable but difficult. Props to qemu, Wine, DXVK, etc.

astlouis44 an hour ago | parent | prev | next [-]

Anyone think this could be used for translating native Windows games over to WASM? Would need to add WebGPU rendering to this

Aurornis 4 hours ago | parent | prev | next [-]

Seeing minesweeper recompiled to run in the browser is awesome.

The pain points about threading in the browser and debugging wasm are the two problems I ran into on another project. I hope we can get some improvements in both areas because wasm would be a lot easier to work with if developers didn't have to fight both of those topics.

valorzard 4 hours ago | parent | prev | next [-]

I also tried doing multithreaded wasm in Rust, and I got some weiiiiird errors. Definitely not stable yet.

arka2147483647 3 hours ago | parent | prev | next [-]

I miss the days when this would just have been named win2wasm.

These branded projects become difficult to remember when everything has a random non-mnemonic name.

theanonymousone 3 hours ago | parent | prev | next [-]

This is a big deal when/if it's working, to me at least. Where can I contribute?

univrsaltrans 2 hours ago | parent [-]

https://github.com/evmar/theseus

Looks like just enough was supported to run minesweeper. Impressive though.

gigel82 3 hours ago | parent | prev [-]

An interesting learning project, but not actually usable.

https://github.com/danoon2/Boxedwine looks interesting in this space, but unfortunately it can't really run anything remotely modern in practice (though if you're looking at 20th century Windows software it will likely be capable of running it).