Yewban

Frontend in Rust🦀️ and WebAssembly.

Motivation

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.

Development process

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

I read through the short docs on Yew. It took me a while to realize that the recommended examples on their github are described in their “next” version of the docs, where they recommend trunk for building the wasm/js bindings. Even the few months old blog post is using a different process. One can really feel that this is still an evolving technology. At the same time, one clearly sees how much better and easier the building process got, from the older tools mentioned in the yew docs all the way to the quite new trunk crate. I can say that using trunk was a breeze. Bellow I write down the common commands and it should be obvious how intuitive and strait forward it is. This was definitely a pleasant and appreciated surprise, since I remember reading some months ago that the rust wasm packaging for javascript was quite tedious.

Yew as a framework itself was also a pleasant surprise.

  1. It is very similar to react, so the concepts were very easy to pick up.

  2. There were almost zero issues regarding fighting with the compiler.

I write TypeScript every day at work and since it’s just a superset of JavaScript, it’s a syntax that lets you get away with allot. So you tend to write the code fast, but later you have to search for bugs if they arise. This is a feature not a bug. Websites are not rocket engines. Employers don’t want to spend 5x the amount of money/time to put the same buttons on the screen, just because there could be less time needed for bug fixing. I totally understand this and that is why nobody expected rust to be a viable frontend language. I am not an expert on this topic to present some definite answer. But what I am trying to say, is that I expected much more pain and surprisingly there was none. Not even a little, really none. Well actually, there was 1 compiler error. I wrote down the short 80 lines of code that create the logic of putting stones into the grids and the one and only compiler error I had was that I didn’t use the “move” command before the closure. It took a moment to realize why it is necessary, but even without the realization, the compiler itself suggested to solve it that way. Prior to writing the 80 lines, I also took some inspiration from the game_of_life example. That also helped.

Gimp

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

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-cli package will be installed as part of trunk.

install the rust wasm toolchain

rustup target add wasm32-unknown-unknown
  • Otherwise trunk build won’t go through.

Development

Build

trunk build
  • This creates the /dist folder. Under the hood it also runs cargo build that creates the /target folder. So you can still normally use cargo’s check, build or clippy for checking code.
  • Static files:

    The folder /static and its contents are copied via trunk automatically to /dist thanks to this line in index.html:
    <link data-trunk rel="copy-dir" href="static"/>

Build, view and rebuild code on the fly

trunk serve

Sharing the result

  • To cut the total size, get a clean output folder (if previous builds were made). This deletes the ./dist folder.

    trunk clean
    
  • Make a new optimized build.

    trunk build --release
    

If you want to run the html directly, for example on a different system, just launching index.html from ./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:
    • rust tool:

      miniserve ./dist --index index.html
      
    • npm (nodejs) tool:

      http-server ./dist
      
    • usually present by default on linux (run from inside the folder):

      python3 -m http.server
      
Radim Janoš
Radim Janoš
Developer

Related