Does this work for most people? I’d assume it was just my slightly out of date version of the weird browser Palemoon that makes it throw “SyntaxError: missing declaration after 'import' keyword” and stay blank white (https://ibb.co/Bj2HHxH) for me, but a dead comment just claimed it’s broken on both macOS and iOS Safari, and Chrome.
It sounds like it's complaining about the use of ES modules. This script is loaded with `<script type="module">` and uses ES imports (`import App from './App.svelte'`).
Yes, Safari is expected to go bang (doesn't support module Workers).
I target Chromium's featureset — I developed on macOS Edge. macOS Chrome and Android's Chrome WebView work for me too.
It needs a few modern features in order to work:
- Web workers
- WebAssembly
- top-level await (on Web, and in Worker)
- ES modules + imports (on Web, and in Worker)
- requestAnimationFrame in Worker
- WebGL2
- OffscreenCanvas
Most of these were chosen for "ease of development". With bundling, it'd be possible to eliminate the ES modules / await usage. WebGL2 isn't strictly necessary either. WASM + workers are widely supported.
So the primary blocker to Firefox support is support for OffscreenCanvas, and requestAnimationFrame in Worker. There's other options there: render on the main thread instead. Harder to develop (syncing timing + state between threads). Can probably be done performantly with SharedArrayBuffer.
In truth, I did include error-handling to provide a compatibility explanation if the worker explodes. This displays in Firefox. But I hadn't expected the main thread to explode too.
> This experiment relies on a lot of new Web Worker functionality (ES imports and requestAnimationFrame). As such, it is currently only expected to work in Chrome/Chromium-based browsers.
You can write vertex shaders in WebGL to massively parallelize calculating the movement of each particle. This isn't possible in canvas.
And that just optimizes the physics—there's even more parallelization with the actual drawing! Drawing a bunch of points like this can be achieved using a single fragment shader that takes a big array of positions. Drawing new positions involves just copying the array of positions from the CPU to the GPU.
But if you're using both vertex and fragment shaders, the GPU might entirely handle updating and drawing points over time (and thus can be parallelized across hundreds of cores), only needing the CPU to set up the initial point positions and get things started.
With canvas, all the work (including rendering each frame) takes the JS main thread, running on the CPU.
Here's a cool demo of how much better WebGL is at rendering a ton of 2D sprites! Be careful turning up the number of sprites with canvas—it might lock up your main thread and be difficult to undo.
They aren't doing the particle sim in WebGL. That's the WASM part.
As an aside: there are new standards in the works for getting even the CanvasContext2D rendering off of the main thread. On an HTMLCanvasElement, you can call transferControlToOffscreen(), which will retrun an OffscreenCanvas object. That object can then be transfered to a Worker. From there, you can create the 2D rendering context (make sure you don't create any 2D contexts on that canvas in the main thread) and draw to the canvas in the worker (you can do this for WebGL contexts, as well).
All the drawing ops performed on the OffscreenCanvas will appear in the HTMLCanvasElement immediately, without having to perform a callback message from the Worker back to the main thread.
Unfortunately, OffscreenCanvas is still in development in Firefox, only accessible behind a flag. In my testing with the feature enabled, it also seems that Mozilla hasn't implemented 2D contexts in Workers, only having the WebGL context available, which is quite annoying.
I don't know the status in Safari, but Apple has historically been very slow about implementing new features.
I use it for text rendering, myself. Getting text rendering working in shader code is pretty difficult and gives pretty low quality results. You have to create a glyph map for all the characters you plan to render--which also means you have to plan ahead of time what text you're going to render--and you are stuck with only one color for all of the text (there are some multicolor SDF-based systems, but they aren't very common).
So I render the text in a 2D rendering context, with all the fancy bells and whistles and support for changing fonts on the fly and colors and transformations and a sane API, in a Worker, save off an ImageBitmap object from it, transfer that ImageBitmap back to the main thread, and use it as a texture on 3D geometry.
I also use 3D contexts in Workers to do some pre-processing on textures, like converting equirectangular skymaps into rectalinear cubemaps. The worker is setup to be able to preload these images while the user is doing other things, then transfer the results when they are needed during a scene transition, so the scene transition is nearly instanteous.
GPU on the web is unfortunately kind of fragmented at the moment. WebGL 1 has pretty broad support but has some technical limitations; WebGL 2 (which this uses) is supported in most things but not (stable) Safari or iOS-based browsers[1]. WebGPU is apparently around the corner but it will likely be years before it has broad support.
(I see reports here of it also not working on Firefox, which must be another reason, as FF does support WebGL2 and WebAssembly.)
God damn I can't wait for webGPU. I would really, really like to have geometry stages, compute shaders, RTX, and actual vulkan-style commands, but that's a little too much to ask of browsers. Too many kinds of hardware, even if you pass device checks onto the developer (which they still should).
60 second explanation of the three APIs:
WebGL 1: vertices and triangles are stored on the global GL context object and need to be rebuilt every frame. Shader language is a very goofy, somewhat obnoxious GLSL lookalike. You can write vertex and fragment shaders, which are the necessary ones to render, but also the least interesting. Post-processing is a serious pain. FPS is limited by bandwidth to submit to the GPU and CPU usage can be stupid high.
WebGL 2: improved shader language (much more similar to GLSL, better compilation, better branching), still only two pipeline stages. Render buffers, depth buffers, multidraw, allowing for modern techniques like deferred rendering and efficient shadowmaps. Vertex attribute buffers are their own thing amd dont have to be freshened for every draw call. At this point webGL is similar-ish to native opengl with a bunch of missing features. In webGL 1 you had to do things very differently from a native api, which was very slow, messy, and annoying.
WebGPU:Vulkan came out! And everyone learned that oh, actually having everything on a single thread really sucks, huh. OpenGL has a global context that prevents great threading- have to make draws from a single thread, even if you can construct buffers etc in job threads (minor improvement) you have to sync back. Vulkan and webGPU let you submit to command buffers with their own contexts. In WebGL 1 you had to rebuild and rebind all of an object's vertices every time it had a drawcall. In WebGL 2 you built them once and just have to rebind each one in series for every frame. In WebGPU, you can bind in parallel from webworkers. Not ideal parallelism, but shockingly good for javascript. For some things like shaders, you don't even need to recompile, which is a hard-to-avoid main thread blocker in WebGL 1/2.
WebGPU can also do compute, which rules- many complex AAA techniques really require this and don't work on WebGL because it wants to send all its render buffers back to the CPU.
WebGPU is at least a decade away from being available in a way that we can rely on it, they are still arguing what the shading language is supposed to look like at syntax level!
For any serious 3D that isn't displaying 3D renderings on a commerce shop, native is the way to go.
I love how the Web freedom advocacy killed Flash and in the process made native mobile games the new center of indie games as the Web keeps failing to provide a mature alternative to Flash/Away3D.
Zero assurances that a WebGL page actually works, given the browser, OS, and possible list of black listed devices.
No debugging tools, SpectorJS is the only surviving one.
While WebGL 2.0 lags behind GL ES 3.2, WebGPU at least a decade away to even reach WebGL 1.0 market share.
I remember in 2015 when I first saw that go-kart wasm demo. It was exciting. But that was such a long time ago and we are only now starting to see webgpu roll out. I’m really looking forward to the new golden age of wasm and webgpu.