Frontend in Rust🦀️ and WebAssembly.
Searching for a frontend experience
During my day job I work with APIs, databases and general backend code. Last time I did anything frontend related were tutorials and small projects with react around 2018. So I decided to do something frontend UI related and interesting. My tendency was to use rust and my first idea was to make a regular desktop app with gtk3. I thought that making a board game grid would be a good and simple enough idea. I was fiddling around with GTK3 in Glade, which is a great tool, and was learning about basic graphical setups with Cairo. But it was all still too new to me and I gave up on making a board with Cairo. But regular desktop apps with GTK3 are a great choice for Rust too it seams. I might do something with that in the future.
A few days later I stumbled upon a nice new blog post about a full featured frontend web framework with rust and WebAssembly using Yew. I heard about Yew before so it caught my attention. I started thinking about how I would make a game grid using css.
So I did the grid with css. This part is always tedious, especially when not doing it often, but it worked. I decided that I would simply make the grid lines not visible, put the goban picture as a scalable background, and the stones as images inside the grid fields. In the end, all the pictures I used are png. It would be better with svg, but as a proof of concept this is good enough.
Yew as a framework itself was also a pleasant surprise.
It is very similar to react, so the concepts were very easy to pick up.
There were almost zero issues regarding fighting with the compiler.
Now I had a clickable board, placing a dummy image into a grid. Proof of concept achieved! Felt good, but this doesn’t look like a goban at all. The time came to get rid of the cell borders and somehow fit bellow them a goban grid, where the stones have to sit on the intersections, not in the fields. In the total beginning, I tried to make that somehow work with lines drawn with CSS, but I didn’t find a way how to make that work. I think having one grid virtual and using it for stone pictures and the other grid as a background picture is still a good solution.
As a big linux and libre-software enthusiast, I spent a few weeks last year learning some gimp basics. That came incredibly in handy at this moment. There are surprisingly many great free sources of gimp tutorials. Among other there is for example the excellent daviesmediadesign.
I need only two things on two layers. A good wood (ideally pine) pattern, and a grid. Finding the wood pattern was surprisingly a bigger challenge than I expected. Writing this article is already much more time than I wanted to spend on pattern acquisition. I couldn’t find a free external gimp pattern and I also couldn’t find an appropriate good and free pine wood picture. This is just a concept. I wanna get something slightly better than making the background plain yellow. In the end I used a gimp preinstalled pattern. There are 13 wood patterns and they all look horrible, with the exception of one, my savior called just “pine”. I put it on a 1920x1920 canvas and the result was surprisingly good.
Than View > Show guides, Image > Guides > New by percent. One horizontal, one vertical, and you have the grid middle. A simple google search for drawing grids in gimp told me that there is a tool for it under Filters > Render > Pattern > Grid. It also has a “offset” property, that allowed me to shift the grid perfectly into the center of the guides, while making appropriate space on the border.
The overflowing grid lines on the border were cut with a layer mask. The dots were painted individually by hand with the pencil tool.
And thats it. Export as png. Than just a few google searches for some large go stone pictures. I found surprisingly good ones. And thats it. Add everything into the CSS and I was blown away how great the result looked, compared to how little time and prior experience it took.
The center of both grids were aligned nicely, but as expected, there were some misalignment issues on the edges. That was nicely solved by having in the css
box-sizing: border-box; where a dynamic
padding: calc(var(--size) / 90); shrinks the invisible grid inwards, perfectly fitting the png under it. Holding with any scale of the window. Wonderful!
External Goban library
Now I had a perfectly looking 5-in-a-row game. Almost go. Still I thought lets push just a little more. I want to at least capture the stones. That is no trivial matter. I could try writing this logic by myself, but previously I found this great library that was just under development with only days after the last commit.
I spent some time reading through it, trying to understand it and thinking how to fit it onto my existing code. In the end, I found that he stores the stone values also simply in a vector. Just how I did previously. Than simply mapping them as DIVs into the grid container. I got that out with his “.raw()” method. Mapped that on the board and suddenly I was able to capture my stones! It was like magic. The magic of open source, haha. I found a tiny bug, reported it to him, and he even fixed it in a few short hours. I feel the love for go and code connecting us, haha.
I added the right panel that shows captured stones and I could add much more still. And maybe I will in the future, but for now I feel like the project’s purpose was achieved.
A frontend written purely in rust, under 80 lines of code in
main.rs. Compiled to WebAssembly. Sitting relaxed on my website. It was a great experience and I had many pleasant surprises.
Please feel free to click on the link on the top of this page and place a stone or two.
install the build tools
cargo install trunk wasm-bindgen-cli
- Trunk is really amazing. At the time of writing it is recommended only in the non-stable version of the yew documentation. Trunk mentions that in the future, the
wasm-bindgen-clipackage will be installed as part of trunk.
install the rust wasm toolchain
rustup target add wasm32-unknown-unknown
trunk buildwon’t go through.
- This creates the
/distfolder. Under the hood it also runs
cargo buildthat creates the
/targetfolder. So you can still normally use cargo’s check, build or clippy for checking code.
Static files:The folder
/staticand its contents are copied via trunk automatically to
/distthanks to this line in
<link data-trunk rel="copy-dir" href="static"/>”
Build, view and rebuild code on the fly
Sharing the result
To cut the total size, get a clean output folder (if previous builds were made). This deletes the
Make a new optimized build.
trunk build --release
If you want to run the html directly, for example on a different system, just launching
./dist/ won’t work! If you need to use the
index.html directly (for example in a link on your web), you have to change the 3 addresses for the .css, .js and .wasm inside. That is what I did on this web. Otherwise by default it is searching for them in root.
But locally the simplest way is to serve the folder:
- take the content of
./dist/and serve it with any of these:
miniserve ./dist --index index.html
npm (nodejs) tool:
usually present by default on linux (run from inside the folder):
python3 -m http.server