Tetris clone written in Zig running on WebGL and WebAssembly
raulgrell.github.io
raulgrell.github.io
That being said, Zig is an interesting, well-deigned language. Despite being young, it already has tons of features. And compatibility with the C ABI is a big plus.
The Tetris example is under 60 kBytes (compressed), and that includes all data as well (there's a sprite/font sheet embedded in the wasm blob). This is in the same general size range as 8-bit BASIC programs.
That means that Zig doesn't need a big runtime to work, similar to C, but unlike C# or Java (or even C++ / Rust, unless you ignore most of the respective standard libraries and restrict yourself to a minimal "embedded-programming" style).
Here's a similar example (shameless plug), a WASM C64 emulator in under 64 KByte download size. This is implemented in C:
https://floooh.github.io/tiny8bit/c64.html
I can (most likely) get to the same result in Zig, but with a much nicer "better C" language (and build system!)
(...of course there's a huge web browser dangling off the WebAssembly blob, but this is mostly runtime components that are not even needed for this type of WASM programs (we really need smaller, more modularized browsers!).
Even in C you must restrict yourself to the runtime your building on, i.e. your target might not have libc. Rust and C++ are the same in the regard that they are capable of doing this.
Both C++ and Rust make it quite easy to "accidentally" add bloat indirectly through their standard libraries because they encourage high-level abstractions which may "unfold" into a lot of binary code from very little source code.
It can be avoided but requires to give up on many of the high-level language features which Rust and C++ differentiate from (for instance) C.
> Rust make it quite easy to "accidentally" add bloat indirectly through their standard libraries
This isn't accurate, the #![no_std] feature disables stdlib in your library/binary. It requires you to implement a few things, but it will not compile if you use things in std [2]. You may be interested in reading the embedded book[3] which discusses this area in a lot more detail.
> give up on many of the high-level language features
I also would disagree. You still have a powerful type system, match statements, RAII, hygienic macros, proc_macros, all of the core library[4], optionally have access to the alloc crate[5], cargo, simple unit-test integration, and a growing set of #[no_std] libraries with easy access through crates.io or git dependencies. There's a lot more of course. Point being, there are still a lot of higher-level language features available even in a #[no_std] context, and it's very easy to ensure that your binaries won't compile if this is a requirement and someone tries to pull something in that relies on std.
[1]: https://blog.rust-lang.org/2018/08/02/Rust-1.28.html
[2]: https://doc.rust-lang.org/nomicon/beneath-std.html
[3]: https://rust-embedded.github.io/book/intro/no-std.html
React supports code splitting, which lets you split large amounts JS into separate production JS files; load only what’s necessary at first, and load the rest as-needed: https://reactjs.org/docs/code-splitting.html
With a new programming language (like Zig), I would hope that they eventually try to have code splitting be automatic and transparent.
For JS targets, you create multiple smaller final JS files instead of one giant JS file. For native targets, you could break out extra code into dynamically loaded .so or .dll files. The real challenge is making this whole process as painless and transparent as possible for the developer.
Not loading everything that's eventually needed at startup can be handled with loading WASM modules on demand (which can be handled the same way as loading DLLs in a native application).
IMHO it's better to fight code bloat at the root, by picking a language without runtime, minimizing dependencies, and picking the right dependencies.
Original Tetris was under 32k, and competition implementations since have been under 512 bytes.
60K plus webkit plus OS is plenty of bloat still
https://github.com/MichalStrehovsky/zerosharp/blob/master/ef...
Zig is aiming to be lean-and-mean, not feature-rich, but I take your point. I agree it's very impressive how quickly it's come this far.
It also gets brought up a bit with the Pharo Smalltalk website(lack of code snippets), but Smalltalk is such a radically different language than what we're mostly familiar with (C, Java, Python, JS) that showing a code snippet is mostly pointless. People try to bring this up, but to no avail.
Sometimes the syntax just isn't very important. I think in Zig's case though, that it is fair to ask.
https://ziglang.org/documentation/master/
The install seems to also be a lot more straightforward than many of the other low level languages that I have tried.
var file = os.File.openRead(arg) catch |err| {
warn("Unable to open file: {}\n", @errorName(err));
return err;
};
defer file.close();
The rest should be basically string operations...https://ziglang.org/#A-fresh-take-on-error-handling
But the one I posted is from GitHub, where it has e.g:
https://github.com/ziglang/zig/blob/8139c5a516eaa217ed76acdf...
also:
https://github.com/ziglang/zig/blob/92d9cef07116da8639ac33b7...
I can follow along with almost any language if there is enough examples I can piece together. I didn't think about scrolling through a bunch of GitHub pages. It isn't as convenient, but probably a better example of how to put together a full program as well as style.
window.addEventListener("keydown", function(e) {
// space and arrow keys
if([32, 37, 38, 39, 40].indexOf(e.keyCode) > -1) {
e.preventDefault();
}
}, false);
That will prevent the scrolling when pressing the arrow keys. It's of course not a perfect solution, and something more robust would be to check if a game is currently running and so on, but it works as a quick hack.Looks like not working in Safari 12.1 in macOS High Sierra :-(
IMHO it's understandable that the author assumed that WebGL2 is available everywhere.
I believe I independently discovered this concept of a tetromino bag. I've been calling it "Gambler's Accurate Model of Reality" in a homage to Gambler's Fallacy.
https://github.com/raulgrell/tetris/commit/4305cd1ac1bcc9443...
That commit is from Feb 7, 2016, and you can see all the TODO comments for the zig features that didn't exist yet :-)
Uncaught TypeError: Cannot read property 'viewport' of null
at env.js:17
(anonymous) @ env.js:17 Uncaught (in promise) TypeError: WebAssembly.instantiate(): Import #0 module="env" error: module is not an object or function
Chromium Version 74.0.3729.108 (Official Build) Arch Linux (64-bit)Safari?
Do you know how easy/difficult would it be to make a desktop version of it ?
One of my long term goal is to make it cross platform.
My plan is to integrate my changes into the original project so that it can export to desktop and web.
I'm following you on Github then.
It's still fun though. :)
Edit: It's definitely on purpose.
Works smoothly.
Compilation to WebAssembly bytecode is done via the new WASM backend in LLVM (the Zig compiler sits on top of LLVM).
The whole interfacing with WebGL and WebAudio is actually quite interesting. It's not done through a runtime provided by the compiler SDK (as it is usually done with emscripten), but instead the Zig-to-JS binding is all done "manually" in the project itself.
There are 2 small "shim files" for WebGL/WebAudio and DOM access written in JS here:
https://github.com/raulgrell/tetris/blob/master/env.js
and here:
https://github.com/raulgrell/tetris/blob/master/dom.js
...and associated Zig interop files here:
https://github.com/raulgrell/tetris/blob/master/src/webgl.zi...
and here:
https://github.com/raulgrell/tetris/blob/master/src/dom.zig
It's really surprisingly little (and clean) code for doing that sort of thing IMHO.
Do you know of any good resources for 2d game programming in WebAssembly?