Assembly script is pretty interesting as a language. It's close enough to type script that the transition from that should be relatively easy for people already using that. And performance wise it gets them a lot of benefits.
There are a lot of other languages coming to wasm. It looks like garbage collection is stabilizing. E.g. jetbrains released an experimental wasm compiler for Kotlin recently that uses that. Currently that only works if you launch Chrome with some experimental flags to enable the feature. But at some point that will be generally available there, and in other browsers.
So, whether you are using C#, Kotlin, Swift, Crystal, or any other garbage collected modern language, chances are there will be a wasm compiler for it soon (if not already) and rich ecosystem of libraries and tools. I see this as a matter of time. Right now it's all a bit cutting edge. So, the whole space is dominated by languages not in need of garbage collection (C, C++, Rust, etc). However, that will change pretty soon.
E.g. Swift and Kotlin are already used for modern UI development on mobile. Using those languages in a browser makes total sense. Kotlin actually has a js transpiler and there are some Kotlin specific web frameworks that I've used. With wasm and the inevitable emergence of new UI frameworks targeting wasm and browsers, there might be a little renaissance in web ui development. Things like Figma prove that web uis don't have to be laggy, limited, and driven by css & dom trees.
Why would you need that hardware support? It's not like javascript interpreters need any hardware acceleration for garbage collection. Relative to javascript, wasm with GC is going to be the same or faster.
Runtime size can be an issue; though arguably WASM is going to outperform minified javascript probably for the same code.
Typical SPAs only manage to stay small by simply not using a lot of run-time dependencies. The same trick will work with wasm. If you don't need it, don't depend on it. Simple. Or alternatively, trade off not having to reinvent the wheel against a few MB of dependencies. In an age where people watch 4K youtube videos, pulling in a few MB is not that big of a deal anymore.
And incidentally, the lack of any sizable run-time dependencies also makes replacing those not that big of a deal. E.g. most of the React ecosystem involves a lot of compile time dependencies (hundreds of MB typically) but not a whole lot of run-time dependencies that people actually ship with their applications. A few hundred kilo bytes typically. I've used alternatives for React that only measured a few thousand lines of code. There's not a whole lot that those run-time libraries actually do that is that essential.
I've noticed with Kotlin js that there isn't a whole lot that I need or miss. Actually, even just having the Kotlin standard library is already a big upgrade relative to what browsers and Javascript have without any dependencies added. Add co-routines to the mix and few other multi platform libraries (e.g. kotlinx.serialization, kotlinx.datetime, ktor client) and it starts feeling like any other modern Kotlin stack that I've worked with. Except it all works in a browser. Is there a size penalty. Yes. Does it matter that much. Not really.
> And performance wise it gets them a lot of benefits.
I am not convinced about that for two reasons:
1. Good money has been invested into making JS fast. If you look at actual benchmarks it is currently quite easy to end up in a situation were your WASM-based solutions might perform WORSE. So at least for now the performance advantages are not that great to begin with.
2. On the frontend, you still need JS to use the DOM. Getting data back and forth from JS to WASM can become a bottleneck. Plus rare processing speed is rarely an issue on the frontend and IO-bound stuff is something JS is already good at.
If you don't do frontend stuff, you don't have to use WASM, you know? You could just go native binary in the first place especially as modern languages have great cross-compilation support. (Edit: If you are after performance mainly and don't care about sandboxing and other advantages.)
Yes, if you need to do some image processing on the frontend or want your users to mine crypto for you, yes WASM has already uses but generally I am a bit skeptical.
Now WASM is awesome for getting you the C/C++/Rust world in the browser, sure. The performance improvements might be overrated though considering the additional development costs compared to just using JS.
(That was more a dig at the WASM hype in general, of course. Not meant to throw any specific shade at AssemblyScript. Still an interesting project regardless.)
1. While true, in practice what I have tested it is that the performance is very, very rarely worse (although often it is negligible difference). The thing is wasm performance is predictable. This is really important for graphics and games since there is no JIT going on, JS (and python, ruby, etc) performance can vary widely based on how often a codepath is used
2. This is planned to change with Interface Types which will let you call any DOM method from any wasm code without without custom bindings and performance hit as well as, for example, calling a python module from golang for example (as long as both are compiled to wasm)
> If you don't do frontend stuff, you don't have to use WASM, you know? You could just go native binary in the first place especially as modern languages have great cross-compilation support.
the main point of wasm outside of browsers is sandboxing and compatibility, a whole set of applications become possible when you have that. A simple example are SaaS application that let users upload wasm plugins that run on the SaaS application servers
> If you don't do frontend stuff, you don't have to use WASM, you know? You could just go native binary in the first place especially as modern languages have great cross-compilation support.
wasm is just one part of the puzzle, the other one is WASI which is poised to become a standard interface for OS interaction. With it you could conceivably have a single binary for any OS+processor architecture (as long as there is wasm runtime + the parts of WASI required by your app implemented). This is huge for embedded development, I myself spent insane amounts of time trying to get shit compiled to different OS+architecture
> If you don't do frontend stuff, you don't have to use WASM, you know? You could just go native binary in the first place especially as modern languages have great cross-compilation support.
- Cross-compiling is perhaps easier these days, but it's still a pain to set up and takes time to compile
- WASM has compelling sandboxing abilities, I can see myself choosing a slower sandboxed client tool vs native binary, in particular to try out new stuff
> Computation-heavy logic like image manipulation, hot game logic, specialized algorithms, emulators, compilers and the likes are great use cases for WebAssembly, and as such for AssemblyScript as well. In some situations it may also be preferable to ship bytecode instead of minified JS, or just the ability to utilize a TypeScript-like language may open up new opportunities, for example for embedded scripting or plugins.
That doesn't really answer the question of why you would want to use AssemblyScript instead of eg C++ or Rust.
It's not as if you get any of the benefits of Javascript, except for a familiar syntax.
It's somewhat subjective, but the core of Typescript which AssemblyScript uses for 'inspiration' is a much smaller and 'friendlier' language than C++ or Rust. Unlike C++ or Rust you can learn the whole language in an afternoon or at most a weekend, and if you already know TypeScript anyway you also already know AssemblyScript.
Basically, the question "when targeting WASM, why should I use C++ and Rust instead of AssemblyScript" is just as valid.
In the end the language choice is most likely enforced by what libraries one needs to use for a specific project anyway (e.g. if you need to build a project on top of a handful C++ libraries, it's probably a good idea to use C++ for the whole project too - and at least there is such a choice now when targeting the web).
> Basically, the question "when targeting WASM, why should I use C++ and Rust instead of AssemblyScript" is just as valid.
…but, if you want to write typescript then why not just write typescript?
If you want better performance surely tier 1 support for wasm, for example, for typescript, it a categorically better solution for everyone, with no need for an entirely new language?
A good comparison is rust-gpu: this is an approach to let you write gpu shaders in rust. The toolchain is different, but you write rust. You compile it. It runs on gpus.
Not everyone needs, or wants to do this, but for people who need a) very high performance, b) existing tooling, you can do it, and it saves you writing shaders in c.
When rust releases a new version, the same rust compiler compiles new rust code and new rust syntax for gpu.
So… the parallels are clear, except assemblyscript seems to have missed the boat by deciding to invent a new language with high level similarity but not quite the same” semantics.
..and when typescript continues to evolve? Will assemblyscript forever be playing catch-up to new typescript syntax? Or fall behind the rentless march of the Microsoft/typescript release cycle?
What about when Microsoft decides to make this a first class feature of typescript itself? I think that’s not really that unlikely if it means significant performance gains.
I feel this is a missed opportunity. What you wanted was to be able to compile typescript to wasm, not a new language.
Typescript's main design goal is not to be a statically typed, compiled language but to gradually add type hints to massive legacy Javascript code bases. This is a very different goal than just having a simple language which compiles to WASM, but still looks familiar to web devs, and I bet if Typescript would add WASM output as official feature, it would have to restrict itself to a 'static' subset - it could never be all of Typescript, because this also would include all of Javascript, including all the 'dynamism' that's hard to bring over to WASM.
TypeScript is an extremely dynamic language which supports dynamic typing and has a very flexible type system. It has a type system that's designed for human productivity, not for machine efficiency. Compiling Typescript down to wasm in an efficient way isn't really possible. Modern JavaScript engines are the fastest ways to run a TypeScript-like language, there's very little if anything to be gained from compiling to wasm.
There is something to gain from writing in a much lower level language though, be it C, C++, Rust, or, yes, AssemblyScript.
TypeScript has completely different goals. It will always just be a semantic checker on top of JS and nothing more. This has been mentioned more than once:
Typescript doesn't have semantics on its own. It's a syntax for JavaScript with a static type checker on top. A lot of JavaScript isn't really suitable for a low level language because of GC, heavy use of very flexible objects, any, only a number type instead of int8/16/... and float32/64, ...
Assemblyscript is essentially a very restricted, low level variant of TS.
If you really want JavaScript in WASM, you can already get that by using Spidermonkey compiled to WASM as an interpreter. A compiled version of TS could be interesting, but would be a lot of work and would come with a lot of difficult challenges for getting could performance.
I will say though that I'd much rather use Rust than AS though, so I do see how the language has limited appeal.
AssemblyScript is nice for small units of code that you might want to accelerate in a larger TypeScript project. The mental context switch is much less because the syntax and some of the tooling is the same, you only have to adjust for the semantics.
It isn't designed for or suitable for large projects.
The other reason to use it is that it's much closer to WASM than Rust or C++. It's the closest you can get to writing WASM without writing WASM.
> It isn't designed for or suitable for large projects.
Why? The compiler itself is written in AssemblyScript and is capable of bootstrapping. The AS code base is quite large. In addition, there are other quite large projects. For example: https://github.com/acutmore/type-off. It's port of "sucrase" project (which functionally equivalent to esbuild or swc)
Easier toolchain, right now targeting WebAssembly with most backends it still a kludge, of duct taped tools, while AssemblyScript is a npm install away and the familiar syntax is a big win regarding adoption.
It's pretty amazing just how bad the tooling still is for Wasm given how much it's been pushed. (I'm a fan of wasm conceptually... I just can't handle all the Emscripten-kludge)
Compared to similar compiler toolchains like the Android NDK or the MSVC command line tools, Emscripten really isn't that bad (for a C/C++ toolchain). Dramatically improving things would most likely mean becoming incompatible with mainstream C/C++ compiler toolchains (after all, the Emscripten compiler and linker needs to be pluggable into existing C/C++ build systems).
It isn't as bad as Symbian C++ used to be juggling MS-DOS batch files, Perl, make and Carbide/Metrowerks project files, but it could have less third party depedencies, between Python, node, Java, cmake and make.
Ideally targeting WebAssembly from C and C++ compilers should be a -march switch, and Emscriptem would fade out.
It's basically clang plus wasm-opt and some magic pixie dust which enables some of the most important features of Emscripten, but without the whole 'technology zoo' :)
The component model [1] with interface types will enable nicer tooling for things like linking and combining different languages, but it's been very slow moving and is still in flux.
Assembly script is a lot easier to pick up than C++ and Rust; especially for people coming from a typescript or javascript language background (i.e. web developers). And it's not like you are going to get a huge performance benefit using Rust or C++ over assembly script. It's the compiler that makes things fast and the idioms. And basically the compiler toolchain is very similar for assemblyscript.
The only real difference with assembly script is that it's a garbage collected language. Otherwise, it uses llvm and the same set of optimizations you'd be getting with C++ and Rust.
What really counts for performance on modern CPUs are memory access patterns, if those are bad, LLVM's optimizer passes won't help much - especially since WASM itself is fairly highlevel too and requires further optimization when translated to machine code anyway.
A more interesting question is instead: does AssemblyScript allow the same control over memory layout as C, C++ or Rust?
I don't think I agree. A debug build of Rust code is often like 40x slower or so than a release build. Those optimizations make a huge difference. Binaryen can only do a fraction of them, so I wouldn't expect AssemblyScript to get close to Rust and C++ (and the benchmarks show that too).
> does AssemblyScript allow the same control over memory layout as C, C++ or Rust?
There's no UB, so in a way AssemblyScript is almost more low level than those, as you have full control over the memory and instructions.
All of that boils down to quality of generated WebAssembly opcodes, nothing prevents an hypothetical AssemblyScript compiler to make use of better optimizations, or being based on LLVM and enjoy the same optimization passes on LLVM IR.
> A debug build of Rust code is often like 40x slower or so than a release build.
Hmm ok, yeah. C++ with heavy stdlib (or generally: template) usage has the same problem (although in my experience it's more like 10..20x slower), but OTH C's debug and release build performance is usually much closer. I guess it's a side effect of generics that they heavily rely on the compiler optimization passes to turn the resulting 'generic mess' into something closer to what a human would write (e.g. remove all the redundant and dead code resulting from 'template resolution').
The main advantage of using Binaryen directly bypassing LLVM is firstly very fast codegen and optimization (Binaryen architecture perfectly utilizes multithreading as opposed to LLVM). In addition, clean and fast debug builds are not clogged with SSA and mSSA noise. That makes debug/release performance not more than 2-4x slower, unlike LLVM where this ratio is 20x-40x as already mentioned. The Binaryen optimizer is improving all the time. It is already better in many places than the same Go's codegen
> [...] For example, TypeScript tooling can be used to author and refactor AssemblyScript code and, with some effort, the same code base can be transpiled to JavaScript with tsc and compiled to WebAssembly with asc, or code shared. The AssemblyScript compiler itself is portable.
I have used Go’s web assembly target a fair bit. The output wasm has an initial overhead of about a megabyte, and goes up pretty rapidly from there. This really limits the cases I’d like to use it in. Beyond that, the performance is generally several times slower than native.
I’ve had AssemblyScript recommended to me for exactly these reasons, but the one thing Go does have is a pretty great library ecosystem that AssemblyScript lacks.
importing stdio printf gives almost no added binary size
importing iostream cout << adds like 5mb to the final .wasm file from what I remember. I asked around and apparently it pulls a whole sort of random stuff into your binary like internationalisation and currency conversion (dots vs commas)
Your native binaries do that as well, you just don't notice. Unless the compiler can offload some of these things to the OS-specific libraries
Fantastic, thanks. I see Swift has everything checked green (full support?) so I shall dig in a little deeper there. But I’m also interested to see whether JavaScript (in progress) will outperform (perf and memory) the JS VMs that already exist and have been optimized for years.
There are a lot of other languages coming to wasm. It looks like garbage collection is stabilizing. E.g. jetbrains released an experimental wasm compiler for Kotlin recently that uses that. Currently that only works if you launch Chrome with some experimental flags to enable the feature. But at some point that will be generally available there, and in other browsers.
So, whether you are using C#, Kotlin, Swift, Crystal, or any other garbage collected modern language, chances are there will be a wasm compiler for it soon (if not already) and rich ecosystem of libraries and tools. I see this as a matter of time. Right now it's all a bit cutting edge. So, the whole space is dominated by languages not in need of garbage collection (C, C++, Rust, etc). However, that will change pretty soon.
E.g. Swift and Kotlin are already used for modern UI development on mobile. Using those languages in a browser makes total sense. Kotlin actually has a js transpiler and there are some Kotlin specific web frameworks that I've used. With wasm and the inevitable emergence of new UI frameworks targeting wasm and browsers, there might be a little renaissance in web ui development. Things like Figma prove that web uis don't have to be laggy, limited, and driven by css & dom trees.