Hacker News new | past | comments | ask | show | jobs | submit login
Learn WebAssembly by writing small programs (github.com/emnudge)
468 points by todsacerdoti on Sept 5, 2023 | hide | past | favorite | 81 comments



Because I've read it here and elsewhere quite often: What is missing from WASM in the browser isn't "just" DOM access, but "everything" else - including fetch or XMLHttpRequest - too. Here is a list of all web APIs supported by browsers and not supported by WASM, the DOM is just one of them: https://developer.mozilla.org/en-US/docs/Web/API

But at least there is hope, that these interfaces will be available once GC is final and supported by browsers:

    Once GC is supported, WebAssembly code would be able to reference and access JavaScript, DOM, and general WebIDL-defined objects.
Last paragraph at https://webassembly.org/docs/web/


GC proposal is from 2018: https://github.com/WebAssembly/proposals/issues/16 and there’s code: https://github.com/WebAssembly/gc/blob/master/proposals/gc/O...

Seems like an awefully long time for progress to be made, given all the possibilities it would unlock.


Yes, but it is now (for some months) supported by V8 (browser, Node and Deno - hidden behind a flag) and Wasmedge and Wasmtime will both have implementations soon.

But I wouldn't hold my breath to being able to call web APIs directly from WASM ;)


If they "solve" that then they risk being the next Java Applets.


Funny how have things have progressed. We went from using HTML + CSS for simple presentation of information, to enriching with small amounts of client side scripting, to plug-and-play sandboxed applications with Flash/Java. Then we realised those were a terrible idea, Flash and Java Applets died, so we started treating JavaScript as a compilation target to mount behemoth abstractions enabling a native application-like experience. When browsers collapsed under the weight of all that crap, WASM was born by necessity. And so we're back to applets.

Am I getting this right?


Our current web standards are ill-suited for what we really want out of the web (rich desktop-like GUI's), so people keep trying different things in desperation. One thing not really tried yet is a state-ful GUI markup language standard. Also, maybe applets could be done right on a second try, applying lessons of the first.


I haven't been following webassembly, but as the former 'cure' for JS, why has it taken so long to get the basics?


It's niche. Front end devs want to use JS or equivalents, not back end languages. And back end devs don't want to browser stuff. So only full stack devs left


Looks very similar to the Exercism model, which also has a free WASM course filled with small exercises[1]. I wonder if the author considered contributing to that course or working together with them, it might get their work to a broader audience and leverage the existing toolset Exercism has to offer.

[1]https://exercism.org/tracks/wasm


I really like Exercism! Unfortunately their exercise model is considerably more free-form where it teaches you considerably less and in much larger chunks. I think this is a great model for certain contexts, but the format I have in the repo is more similar to "rustlings" and "ziglings" where you're taught syntax and features alongside the code examples.

I don't know that their Wasm module is necessarily "broken", so I'm unsure whether my contributions would be welcome.


Very fair! It's been close to a decade since I used Exercism so I don't really know what their stuff is like nowadays. From what I remember, the focus was less on teaching you the language directly (via instructions) and was more on encouraging the user to reiterate on their code and experiment, and letting reviewers lead the direction of growth.


One thing I don't like about Exercism is that except for the most popular languages, the exercises are often not "sysematics".

In other words, it's just a bunch of leetcode-like questions, ordered by difficulties.

A proper course should order the exercises by language features. Exercism actually has built a fantastic interface for this[1], but not utiltized it for most langauges.

[1]: https://exercism.org/tracks/csharp/concepts


I completely agree with this. I love the platform, the mentors, and the enthusiasm for languages. They’ve done a great series every month this year on a new set of languages, and have great YouTube interviews.

However, the problems they offer do not help you learn the syntax or quirks of specific languages and for me are only a place to practice leetcode/codewars style logic. Also important! But not what I need when I first begin a language.


But leetcode has this https://leetcode.com/studyplan/


This is great, thanks!

One of my favorite ways to learn a new language or framework is via "koans"[1], which this reminds me of. It's a gentle ramp up from basic to advanced features, and the TDD-like workflow of seeing a test fail, understanding why, and fixing it, is really conducive to learning, while giving you that dopamine jolt from "a-ha!".

[1]: https://github.com/ahmdrefat/awesome-koans/blob/master/koans...


sounds like a great way to learn a language "on your own". unfortunately, Rust is missing. can anyone propose a link?


It credits Rustlings at the end of the page linked to (https://github.com/EmNudge/watlings#credits).


Isn't that basically Rustlings?


i think you've found what i'm looking for! https://github.com/rust-lang/rustlings


Just a remark: if you want to play with WASM features like GC, use Binaryen's `wasm-opt` instead of WABT, as `wasm-opt` supports way more WASM extensions.


Pretty cool.

I haven't really explored WASM hands-on (I'll give this guide a try) but, given that it's already been a few years, I think it's been hugely beneficial for web development.

Not the "JavaScript killer" some where hoping for, though it was never meant to be one. Instead it integrates pretty nicely within the existing ecosystem, optimizing existing use-cases and allowing new ones when heavy computations are required. Net benefit for all web devs - faster libraries, impressive dev tools and more portable node binaries.


I've been watching WASM from afar, and it's my understanding that it's not a JS killer because it doesn't have direct access to the DOM or to most DOM APIs. I'm curious if there's another reason I'm missing?


Is being a "JS killer" even a goal of WASM? From what I understand, WASM's goal is to allow computationally heavy workloads (e.g., image and audio processing) to the browser by providing a compilation target for C code. This is how we get nice things like Figma or MS Office running in the browser.

Let WASM do the number crunching, let JS do the UI.


It's not the goal from what it seems. But it's definitely what would make it the most interesting to a lot of people, and it's what I think most people expected at some point.

For a lot of companies the only place JavaScript is ever used is on their website frontend. And it makes you wonder, why does it even still need to be JavaScript? With the rise of SPAs and the fall of normal document based websites, browsers are basically just becoming their own platform like Windows Desktop, Mac, Linux, Android, iOS, etc. You could say it's been that way for a long time, but more and more apps are becoming web based only because the browser is now powerful enough to run what used to be a desktop application.

Browsers are literally just a VM with an address bar. We go to a URL and run a program, except right now there's basically the limitation that the program has to be written at least partly in JavaScript. Being able to deploy an entire website as WASM is just the next logical step from what I see.


The two language problem is, indeed, awkward.

DOM bindings in WASM would be awesome. I would be a happy nerd if I could do all of my web dev in OCaml.

In the meantime, WASM has real, just not for front end dev.

The two language problem is hardly unique to web dev. It’s also ubiquitous in the machine learning/data science space with Python and C/C++/CUDA playing the roles of JS and WASM, respectively.


> DOM bindings in WASM would be awesome. I would be a happy nerd if I could do all of my web dev in OCaml.

DOM is not enough for that. You almost certainly would like to be able to communicate with your backend ;)

Here is a list of the "usual" web APIs: https://developer.mozilla.org/en-US/docs/Web/API And everything that needs network access or access to local resources (file system in the worst case) will never happen to WASM because of security considerations.


How is allowing WebAssembly to use the network any less secure than allowing JavaScript to use the network?


The issue here is the same-origin principle, which rules out most low-level networking (since you could just bring your own SOP-ignoring HTTP client).

Personally I think that enforcing the SOP at all cost (and even when no cookies or other authentication headers are injected by the browser) is misguided at this point and holding back modern webapps.


Don't ask me :).

At least WASM will get DOM access (and hopefully access to similar web APIs) as soon as the GC is stable and usable.

   Once GC is supported, WebAssembly code would be able to reference and access JavaScript, DOM, and general WebIDL-defined objects.
https://webassembly.org/docs/web/


But you will be able to do network access though the fetch API, and fs access though its own API.


> I would be a happy nerd if I could do all of my web dev in OCaml.

You don't like Bonsai? Or do you just want something that doesn't "transpile" to JS at all?


I find that these compile-to-JS languages are great until you want to bring in other JS libraries. Then you’re stuck writing bindings which is annoying, and often brittle if you’re not really careful.


Oh I didn't even think of that--that sounds like a huge pain in the ass lol


You can certainly bridge the DOM to WASM with a virtual DOM. The problems with WASM are not just interoperability.

The biggest problem is tooling. You cannot build tooling for in-browser WASM because it runs in a browser sandbox. JS has the same problem but the difference is that JS has a known object model that the browser can provide good tooling for.

Whereas with WASM the browser has little insight into what those opaque Memory objects contain. So you need to bring your own tooling and run it inside the sandbox, and the sandboxing does not make this easy.

Let’s say for example you want to pause execution, inspect the object tree, and run a REPL while it’s paused.


If it was the JavaScript killer it will attract JavaScript devs and they’ll make it just like JavaScript

not syntax-wise just community wise, where the package and package managers are a mess and why there is demand for a JavaScript killer


Yep. Give access to the DOM - perhaps via a batch update/fragment mechanism and watch as it becomes a JS killer. But WASM is currently a starved prisoner locked up in a room only allowed to view the goodies outside the cell door.


What would that change? DOM interaction can and is abstracted away by frameworks anyways and not performance critical


It doesn't have access to _anything_ (in the browser, in other runtimes there is WASI with POSIX functions). Everything has to be imported from or exported to JS.

And currently using anything but C, C++ or Rust isn't feasible, as the runtime needed for a GC is way too big. A Haskell "Hello World" for example is about 1MB (even after running `wasm-opt` on the generated WASM binary).


I do agree things like the GC are very large if we're comparing against standard JavaScript, so I don't believe the technology is ready for standard customer facing websites. For things like internal applications/extranet type applications, I think that the runtime download cost is minimal compared to the functionality that you are given with a proper framework (I can only speak of Microsoft Blazor, but that's just language ignorance, and I know there are other that fit the model as well). As a web developer that also writes utilities to run on the desktop, for things such as ETL or fixing bad data, being able to not switch to another language or even really framework is a huge boon for my time. I know JavaScript, but being able to rarely have to deal with it keeps my head in what I'm solving, rather than having to context switch between interface and server.


> isn't feasible

This is kind of subjective, and in the context of "is this a JS killer" which is what you're answering, I'd agree, it makes sending the Wasm to the browser a bit of a non-starter that it requires a large bundle most of which is simply boilerplate for whatever runtime, without some type of local storage and persistent cache it's difficult to imagine using Ruby in a Wasm for example. If you're deploying to a container, where you're able to use a cache warmer to ensure the wasm is ready before it's called, then a 1mb binary might not be such a big issue.

(I mean, it is still a big issue because the point was fast cold starts, and big assemblies mean no fast cold starts, but again, subjective value judgments... 1mb isn't too big in many cases and I'd wager most of the cases that I'd really care about. But in a browser...)

But if you're not trying to run the Wasm in a browser then it's still potentially quite useful. You might not be running all of your Wasm in a browser and it might still be a JS killer, and all of those might not be in conflict.


Well, the main reason why I'm taking a deeper look at WASM is because I'm creating a language and compiler that is optimized to compile to WASM. While I don't think that my compiler will be the JS killer, I do think that WASM languages using a (the WASM) GC have a future.


I mean Microsofts Blazor framework can use WASM to run C#(and its runtime) in the browser.


Virgil compiles to Wasm and brings its own GC as well, and the runtime is on the order of about 4KB. The GC is a simple semi-space copying collector and the compiler prunes away dead code from libraries, so binaries are pretty small. So overall I don't think this is a Wasm issue as it is mostly a language runtime size issue.


In the browser, can it integrate with SharedArrayBuffer, worker's postMessage or transferrable offscreen canvas?


Depends what you mean by "integrate". It always has to import or export JS functions or memory. WASM (without WASI) has no direct connection to "the outside World", everything has to be routed via JS FFI. So you can import or export a SharedArrayBuffer to communicate with JS. https://developer.mozilla.org/en-US/docs/WebAssembly/JavaScr...

But you need the WASM thread extension (for atomic access to the shared memory) to be able to use shared memory.


WASM essentially can call javascript functions that were imported, and I believe javascript is able to read WASM's memory (a big help for transferring strings). If you're using something like Rust, all the glue code to call any JS APIs can be automatically generated for you with wasm-bindgen, so it really isn't a huge problem usability wise. It's just not great for performance.


> And currently using anything but C, C++ or Rust isn't feasible

Someone should tell Anaconda that they can't do this, then: https://pyscript.net/


That uses Pyodide. From Pyodide's own pages:

"At present a first load of Pyodide requires a 6.4 MB download, and the environment initialization takes 4 to 5 seconds."

So, yes, it works. But there's plenty of situations where a big download followed by a 5 second stall is a non-starter.


I'm cheered by ongoing progress in adoption of WebAssembly. While Microsoft is often given minimal notice here, I urge anyone interested in WASM to experiment with Blazor WebAssembly. It's an incredibly powerful framework that lets you use C# and most .NET libraries (including NuGet packages) as compiled WASM in the browser.


Re ‘most nuget packages’ What’s the limitation here? If it requires the ability to read a file on disk, does it straight up fail or does the file read interface push the action back into a server-side rendering type action?


The NuGet packages are zip files containing compiled binary and possibly other resources. They're referenced in the source code and included at compile time. Since they become integrated into the overall compile, there's no need to reference them in the final runtime product.


Sorry that misses my point, blazor runs client side which means that it’s sandboxed with the same permissions as JavaScript executed within a page - you can’t use blazor to read a file client-side, but there’s nothing to prevent wrapping what would be a server side library in a way where ‘it executes on the client except when it can’t, at which point it executes on the server’

Doing so would almost always be a security risk, as validation would presumably still run client side, but that doesn’t mean it’s not possible to actually achieve such an engine.


Like most wasm, if the nugget is just a thin wrapper around a recompiled binary, it's not going to work.

WASM is a compile target. So if the code source is available and it's "pure", most wasm can now compile it.

Check out pyiodide for a extensive python project trying to bring everything in Python to the web.

Your specific concern regarding file access: they just create something like a indexeddb file system if you want to work with files.


From reading about it, it seems to suffer the same problem as Go compiled to wasm: payload is huge, usually multi-MB after compression.


Is there an article comparing other compile targets.

I mostly get the impression that the compiler in browser is the problem.

If it's adopted natively, that should disappear.

All I'd need is Firefox, edge and chrome to switch to publishing wasm.


Cool project!

I maintain a somewhat related repo here: https://github.com/eliben/wasm-wat-samples/


That looks super useful! I wish I knew about this when first learning wat. This would make a great reference!


Gave WASM a try and ran into problems exposing connections to a SQLite database I wanted to connect to locally. Has anyone done something like that before and can point to good resources?


Check out Spin! It includes sqlite support natively in the runtime since 1.4:

https://www.fermyon.com/blog/spin-v14


Looks like it’s only available for Python, I was hoping for a more language agnostic solution. Thanks though!


It is? The docs example shows support for Rust, TypeScript, and Python:

https://developer.fermyon.com/spin/sqlite-api-guide#using-sq...

I'm not sure what holds back from being a completely language agnostic solution, but I know when I tried binding functions into my Ruby app with wasmer I had nothing but problems, I wound up using WASI exclusively to communicate with my WASM modules. I don't honestly know how good the WASM language agnostic story is today. I gave a talk with my perspective as a Rubyist at OSS Summit and CDCon/GitOpsCon a few months ago in Vancouver. The talk (and lightning talk variant) are called "Exotic Runtime Targets: Ruby and Wasm on Kubernetes and GitOps Delivery Pipelines"


It's super interesting to me how much WebAssembly resembles a "real language" that's sorta writable by-hand. I bet it lowers the bar quite a bit when targeting it


Very neat.

As WebAssembly becomes the lingua franca of different ecosystems, having a strong grasp of how it works is worth the time investment. Thanks for this.


Cool. Did anyone try targeting Qt applications for WebAssembly? I wonder how convenient it is to use.


There's an interesting write up here: https://www.linkedin.com/pulse/qt-wasm-nicholas-petreley-1c

Personally, I didn't find the author's sample app to load very quickly- I think it was 10 seconds or so just to get something on the screen- and it was useless on my phone.

That same app would easily perform better and have better device support were it written in js/html/css.

I just don't see a place for things like QT or native gui stuff in real world WASM. Number crunching or fast data processing, sure, or maybe a game, but a standard UI is crap if the whole thing is in a big canvas.


Is there a WA UI framework for web similar to Svelte or Vue?



Hand written wasm is too low level to use a UI framework with, but there are rust frameworks that compile to wasm for web. For example, Yew and Leptos are both web-first frameworks, and there are a few such as Dioxus or wgpu that are native-first and work on web.


What makes hand written WASM too "low level"?

It isn't, really, what the Rust frameworks do is compile down to a specific interop, sure (JS + WASM, its not pure WASM due to lack of DOM access for starters).

That said, no reason a hand written WASM app isn't feasible, with appropriate JS glue


While it's an interesting (and effective) approach to learn something, what is the point of learning WebAssembly? After all, I thought that WASM was supposed to be something like "LLVM for web", a low-level IR for webdev languages to target and for browsers to execute efficiently, and not something that you'd write manually. Or am I wrong?


Actually it is quite high level (with the GC extension even "higher" than C), not comparable to "real" assembler. It has types. It isn't fun writing WAT, but for small stuff it's perfectly doable.

That's a record with 3 fields, its constructor and getters using the GC extension:

    ;; A `struct` with 3 fields, all immutable.
    (type $testStruct
      (struct
        (field $first i64)
        (field $foo f32)
        (field $baz f64)))

     ;; Constructor. `ref` is a reference
     (func $mkTestStruct
       (param $a i64) (param $b f32) (param $c f64) (result (ref $testStruct))
        (struct.new $testStruct (local.get $a) (local.get $b) (local.get $c)))
     (export "mkTestStruct" (func $mkTestStruct))

     ;; Getter function for the three fields:

     (func $getTestStruct1 (param $rs (ref $testStruct)) (result i64)
       (struct.get $testStruct 0 (local.get $rs)))
     (export "getTestStruct1" (func $getTestStruct1))

     (func $getTestStruct2 (param $rs (ref $testStruct)) (result f32)
       (struct.get $testStruct 1 (local.get $rs)))
     (export "getTestStruct2" (func $getTestStruct2))

     (func $getTestStruct3 (param $rs (ref $testStruct)) (result f64)
       (struct.get $testStruct 2 (local.get $rs)))
     (export "getTestStruct3" (func $getTestStruct3))


Author here!

My initial motivation to learn WASM (as someone from a primarily web background) was that I had a pretty poor understanding of WASM in general and so I had a lot of difficulty working with WASM builds in just about any capacity other than a heavy JS wrapper.

There are aspects to how WASM works that are quite different from other kinds of assembly formats that make learning the basics pretty important. e.g. how memory is requested, provided, grown. How functions are received and exported. Capabilities of tables.

A lot of this might be abstracted by massive wrappers, but you're losing a lot in perf and debugability when using them.


Thanks for putting this together, I too learn best by doing, and this looks very helpful!


Sometimes you may need to debug the actual wasm code or understand what the compiler is outputting. Depending on the flags you set during compilation, you may want to optimize for size of binary or for performance. You may also want to compare the wasm produced by two different languages or different versions of the language compiler. Maybe you want to verify what libraries are actually getting bundled with your wasm.


Understanding what the compiler outputs is also essential to debug the inevitable issues that come up when porting an existing program to WASM. Since it's a different platform with significant limitations, it's important to know how to replace some core APIs and capabilities and how to interface with these alternatives.

By limitations I mean things that a POSIX program would expect that aren't generally available from WASM, like network programming, file system access, processes and threads, pipes and signals. Emscripten and WASI help to some extent, but the replacement APIs are rarely fully compatible.


Some reasons to understand the textual representation, besides curiousity:

You are embedding a WASM engine and want to better grok how the import or exporting works.

You are writing a compiler that targets WASM.


For awhile I was using a raw wasm program for a game engine's random number generator. Stored all the state in global variables, so no heap necessary

Eventually I moved the whole game engine into wasm & at that point it all got ported to Rust

https://github.com/serprex/openEtG/blob/8b7069cc52ec8db3406a...


Is it just me, or does "WebAssembly" seem like an oxymoron? Maybe it's because I've been involved with the web since its earliest beginnings.


You might like this bit of speculative history: <https://www.destroyallsoftware.com/talks/the-birth-and-death...>


Thanks! That was fun. I hadn't seen it. There's the problem of bootstrapping METAL, but perhaps left for a different lecture! ;-)


I mean technically it's browser VM bytecode by it's semantics.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: