You Are Doomed To Learn WebAssembly

At first, Web browsers displayed HTML pages. But then people wanted those pages to do something. So we got — among other things — JavaScript. Then people wanted to do super complicated and compute-intensive things. So now we have WebAssembly. If you want to learn it, [diekmann] has a 4-part series that covers everything from getting started to porting Doom into your browser.

Paradoxically, instead of using a browser, he uses the wasm binary toolkit to run code more like a standard assembler. And wasm — what most people call WebAssembly — isn’t like most assemblers you know. Instead of labels, there are blocks that work much more like high-level language constructs such as while loops in C.

If you were expecting the Doom port to be a trip through emscripten, you’ll be disappointed. The fourth installment ports the Doom code directly and that’s very educational. There’s a lot to be done, and most of the steps stay on the regular platform to ease debugging. The switch to browser hosting occurs only at the end.

The first step was to remove some features. Things such as X11 shared memory make sense because you aren’t going to use X11. However, we were sad to see the sound code taken out. At some point, the code has to make the jump to browser output and then you are blind until you get it working.

A lot of missing functions were written using Rust and borrowed from musl, a C library implementation. There’s a lot of debugging, documented as GitHub check-ins. But it does work. Don’t believe it? Try it.

Doom ports are popular, even going to a light bulb, if not a pregnancy test. We prefer, of course, an oscilloscope.

36 thoughts on “You Are Doomed To Learn WebAssembly

      1. Cool Name!

        Also, yeah, JS as language for web development is a disaster.

        It’s used for

        * manipulating state of a very complex piece of software (a browser), especially for a document model
        * offering rich media experiences
        * and be safe to implement in environments where servers and clients, and many different libraries on these, exchange data.
        * Needs to work on a vastly heterogeneous landscape of devices and CPU architectures.
        * Needs to be safe and respect user’s data, and CPU cycle usage wishes.

        Logically, this means that

        * JavaScript has no threading; web workers really badly address that. There are only very awkward ways of synchronization between components. The DOM is largely unprotectable – everything can change, through anything, at any time
        * JavaScript has not even a numerical vector type. Runtime implementations try to do the best with it. But JavaScript’s abilites at actually doing any of the signal processing you’d want in audio and video handling (which doesn’t need to be much) are so bad, that the WebAudio API is just a nightmare, and there’s more than one place that was forced to put trivial signal processing (reduce the volume of that stream, increase the one of the other, don’t send, or send zeros, of my audio) on the server; instead of allowing developers to just, you know, develop applications, they said “these effects are all that we’ll ever need”, and then even these were badly specified. So there’s WebRTC that tries to offer developers a low-latency alternative to doing things in JavaScript, on a central mixing server. Remember the outcry when it became apparent Jitsi was actually sending your audio to the server, where it was then simply not distributed to the other participants in a call, when you clicked the “mute” button, instead of *not* sending your data to an unknown server? That’s a result of how crappy these centralized APIs are. If you think pre-C++11 STL is not that great, JavaScripts Browser API looks like PHP and a bunch of rabid monkeys decided to intensify their relationship by doing library design.
        * JavaScript: has. no. typing. Don’t get me wrong, I love a dynamically typed language as much as the next guy (I use Python a lot), but JavaScript’s type system (including its “nobody else does it that way, I wonder why” prototype-inheritance model) is pants-on-head stupid. Seriously. If multiple parties develop a language to put on top of your language, just to introduce a resemblance of sanity into handling structured data, your language simply isn’t very useful on its own
        * Is the limiting factor in browser performance, so much, that a browser that doesn’t have a target-optimized JS runtime is essentially useless for browsing social media sites (i.e. typical browser use case, let’s face it). This goes so far that chromium/chrome, with the whole of Google being interested in the proliferation of that, took years to port to RISC-V, and it’s an impressive feat how well it runs.
        * Is, at least on my PC, consuming more cycles on twitter’s UI than on compiling stuff and running high-rate signal processing in C++; which I do professionally, on occasion.

        What I *like* about Javascript is how enduringly people work around these shortcomings – asm.js, lobbying for better APIs in gremiums, writing crazily optimized JS runtimes, which suddenly become *very* platform dependent and a lot of work, and re-innovating language practice of the last ~50 years atop of JavaScript in shape of things like TypeScript are signs of dedication.

        1. JavaScript’s runtime is, by virtual of its importance and ubiquity, *lightyears* better than any other scripting language. Yes, you could have even better performance if JavaScript were a little different. But you generally couldn’t get better performance by *replacing* JavaScript with any other language. JavaScript revitalized and returned most of the high performance runtime work done in the 80s with smalltalk etc. To be fair Java was a brief contender for this throne as well, but Java’s standard library architecture doomed it for interactive use.

  1. “Instead of labels, there are blocks that work much more like high-level language constructs such as while loops in C.”

    Embedding high-level language constructs (or providing “blocks” with unknown timing and complexity characteristics) kinda defeats the point of assembly. Weren’t INT 10h DOS BIOS routines bad enough?

    This language is more akin to a shell script or some form of alternate JS than assembly or C. Not sure why they named it the way they did but hey we have Javascript which has nothing to do with Java, AWS Lambda which has nothing to do with Java Lambda, Java Streams which has noting to do with Unix STREAMS, and C# or Objective C which isnt C or C++, and C++ which isn’t C++ anymore anyway. These hipsters ruined it. They just randomly name things, sketch a smelly animal from the zoo, publish a badly-written O’Reilly book, and call it profits. Too much language name “borrowing” and bloat.

    1. No, it doesn’t. The point of assembly is to be able to run it on a physical processor. The point of WebAssembly is that it can be run on any processor, therefore it has to be designed in a way which allows to easily generate from it the actual assembly. Its complexity has to be similar to compiler intermediate languages, such as LLVM IR.

      You seem to have a misguided, outdated view of assembly. On current CPUs instructions do have unknown timing, thanks to caches, memory translation, out-of-order execution, etc. The days of cycle-exact programming are long over.

      1. Um, no. The point of an assembler is to provide the lowest-level access to machine code for a specific processor. The purpose of high-level languages is to be able to run a given program on any processor. WebAssembly is the latter. It is the opposite of an assembler.

        1. How does that work when you run in a virtual machine? The assembly code has no access to the low-level guts.

          And what you said is not even true. Machine code is not directly executed by the processor, it’s turned into microcode on the fly, so the “actual”processor is just a figment of the imagination.

          Assembly code is just a human readable version of the byte stream that’s fed to the instruction decoder, no matter whether the decoder is in hardware or software. There is also assembly language for the JVM.

          1. It doesn’t. Calling this monstrosity “WebAssembly” was just a marketing trick to imply that it would be much faster than other browser-interpreted languages.

            And you’re just being dense when you compare microcode with virtual machines. Let’s be clear about this: the browser has to download the bytecode (or whatever they want to call their intermediate code), and either compile and execute it, or just interpret it. This is done with high-level software, not low-level firmware. Big difference.

    2. Accurate timing assembly down to the cycles is for microcontrollers. There is just too much happening behind the scene with a modern multicore CPU. Even with 20 years old CPU is was impossible. But you are in luck, there are plenty of blazing fast, dirt-cheap microcontrollers around where you still can program that way, and move mountains.

  2. I miss the time when programs would run natively on a device and people weren’t this wasteful with their devices’ resources. These days even the simplest of “apps” run sluggish and unstable on the most powerful devices because they are really just bloated websites with too many scripts. Questionable progress has been made.

    1. Yeah those freeways are also the worst thing ever, had to trade in my model t Ford for something more modern. I’m still upset about indoor plumbing, it cost a lot of money to run those pipes, and for what? Yes Indeed life was much easier when we all just lived in caves.

    2. You could make the exact same argument with HTML (and CSS), but I would be surprised if you felt the same way about those. JavaScript provides a better way to integrate web applications, compared to constantly sending complete web pages to keep the application logic on the server side. Can JS be abused? Sure. What can’t? Can it be subject to incompetence? Absolutely. Again, what can’t?

  3. “You Are Doomed To Learn WebAssembly”

    I’m going to rebel to the bitter end, having to be drug kicking and screaming

    Am going to double down on learning to program FPGAs instead, and then go on to recreate that which the contemporary world regards as antiquated, quaint – obsolete. It will be my solitary act of defiance.

    You modernist can have your fancy browser-as-the-platform dystopia

    One needs to be ale to sleep well at night with a clear conscience – am taking the retro Titanic down to the bottom listening to an orchestra playing on my 1981 vintage Rega Planar turntable

    1. Well, at least WebAssembly is much like retro – kind of forth meets lisp.
      It seems you can either just call things one by one like forth words, or just give a line as a lisp s-expression.

      Now sprinkle some stuff like promises (which is like a forth comment, but with types) so that the wasm client can reject
      code if it looks like it might not leave the stack the way it claimed it would.

      And you’ve got forth except without the clusterf**k that happens if someone mishandles the stack.

      I think it’s kinda neat.

      I mostly work in either FPGA’s ( symbiflow rules!) or just python (for the data-science stuff – usually in jupyterlab with the matplotlib widgets).

      I’m not sure where exactly I’d have cause to use wasm, but this intro was particularly interesting for cutting past most of the excess crap and shooting for a minimal, but workable, solution.

      And I appreciate that. (ie, using xterm.js, but also using unpkg.com to avoid npm entirely, which was cool).

Leave a Reply

Please be kind and respectful to help make the comments section excellent. (Comment Policy)

This site uses Akismet to reduce spam. Learn how your comment data is processed.