tutorial for a minimal example of rust wasm PWA
version: 2021.204.1558 date: 2021-02-04 author: bestia.dev repository: GitHub
what to do?
First I need to decide what to do. It will be primarily a tutorial. But it will be also a useful piece of software. This makes it more interesting, motivating and fun.
The readme.md will have all the steps I took to make a finished app. And most of my inner monologue. Probably the readme.md will be much larger than the source code itself.
A big digital clock on the screen is nice to have. On any device. At least I want to have it. I have a big TV screen connected to my PC that is turned on all the time. Win10 has the clock on the taskbar, but it is miniscule and hard to read. There are a lot of applications with all types of clocks out there on the wild internet, but do you really want to install any unknown software this days? I fear malware, viruses, pandemics and all the rest. Web pages are trust-worthy, because they don't have access to 100% of my local machine, operating system and file system.
So it has to be a web page. I like the rust programming language and it is the best choice for wasm/webassembly that runs inside any modern browser on any platform. And it has great performance.
A little of HTML is mandatory for the user interface. With a little of CSS to make it look fancy. This is than very easy to modify to suit everyone's personal taste and preferences.
Let's make it look like a native application with some simple boilerplate PWA (progressive web app) magic. It is supported by all the modern browsers in all the platforms. And the user experience is great.
I will try to make it minimal as possible so to have an easy starting point for learning about the subject.
It looks like a plan. Let's start.
naming the project
There are two hard things in computer science: cache invalidation, naming things, and off-by-one errors.
What name to give? It will never be the best name. But it is really difficult to change it later. At least I will make it descriptive. This is a tutorial and not a commercial project. Very often the development name is not the same as the commercial name. To make things easier or harder, depends how you look at it.
What is important:
- rust programming language
- wasm compilation target
- PWA resulting app format
- minimal code to show that it works
- showing the clock is the main purpose
So very innovative and imaginative name is:
I choose it to be in snake case style, because it is the default name format in rust. And for a good reason. No problems with filenames, case sensitive systems, spaces inside the name and what not.
I appreciate names that are easy to
find and replace, because this is the only function supported by text editors across heterogenous file types. It is bad to have a name that can be a substring of another name. It makes it really hard to
find and replace across a big project.
I work on a lenovo ideapad flex 5 14 and I am happy. Win10 is my primary OS, but all the programming is made inside WSL2 (windows subsystem for linux), where I installed Debian stretch.
My editor of choice is VSCode with plugins:
- Remote - WSL
- rainbow brackets
- code spell checker
The rust toolchain installation is very easy for Debian on WSL, just follow the instructions how to use rustup on rust-lang.org.
For wasm compilation we need the tool wasm-pack.
You probably found this file you read just now on Github.Github (Microsoft) is for now the place for my repositories.
And Git on Debian is logically the version control system for my source code.
Licence is a thing today and the easiest one is the MIT licence. It basically means it is completely free of everything: money, profit, responsibility, liability and anything. It is a tutorial for everybody. Just use it.
Let start with a simple static HTML and even simpler CSS files. Just to see if the web server works. I like to have a separate web_server_folder and then rust_wasm_pwa_minimal_clock, because of my previous complex web apps. It is a wise choice. \ The code inside my 'index.html' and 'rust_wasm_pwa_minimal_clock.css' will evolve. But nothing too extravagant to not understand. Mainly boring boilerplate copied from somewhere.
web server and wasm/webassembly
We will need a web file server because browser security does not allow loading wasm modules from local files.
Install this basic server:
cargo install basic-http-server
Run the server in a separate terminal so it can stay running all the time. It is good to have a separate folder for the web server. Go to the web server folder and run the server:
cd ~/rustprojects/rust_wasm_pwa_minimal_clock/web_server_folder; basic-http-server
Open the browser on:
If the web app is located on the root of the web site it is difficult to develop and debug. It is easier to make a subfolder explicitly for this web app. Ok. It should work. Just a static html+css for now.
Cargo.toml is very important to define the output as wasm library and the required dependencies to web-sys, js-sys and wasm-bindgen.
We need a src/lib.rs for our code. We start just with a simple wasm_bindgen_start function.
Compile the code with:
wasm-pack build --target web --release
With a little luck we now have a pkg folder with all the goodies.
We must copy it to our web_server_folder/rust_wasm_pwa_minimal_clock. I like to use the rsync utility.
sudo apt install rsync
\rsync -a --delete-after pkg/ web_server_folder/rust_wasm_pwa_minimal_clock/pkg/
Just hard refresh/reload (Ctrl+F5) the browser 2 times and watch the console in F12 developer tools.
PWA (progressive web app)
It is easy to make a simple PWA. We need to add some files and some code and it is done.
- in the folder /icons/ we need a lot of png files with different sizes of the app icon
- in the index.html header there is "A lot of metadata just for PWA"
When updating the files on the server, we must also update the app version number. It is in 2 places:
- service_worker.js - in this constant: const CACHE_NAME = '2020.1206.1112'
- Cargo.toml - in this line: version = "2020.1206.1112"
Now when you open this web app the browser will allow you to
install it and the user interface will be different from there.
To update the PWA hit Ctrl+F5 two times. It will update if it needs to.
It is boring to repeat the same commands every time we compile: change version number, build, copy pkg.
We can automate this with the utilities: cargo-make and the lmake_version_from_date.
cargo install cargo-make
cargo install lmake_version_from_date
In the file Makefile.toml we write the automation scripts.
cargo make - for help
cargo make release - increment version in Cargo.toml and service_worker.js, build release version, copy pkg to content folder
Finally we come to the sweet spot. We have to write some code in rust. This is a very simple example.
If you want to test something small or share some small part of your code you can use the Rust Playground online. It has everything you need: fast, easy and accessible everywhere.
The wasm code starts with the function
debug_write() in the
F12 developer tools console of the browser the name and version of the PWA, just for debug purposes.
set_interval() to execute the function
write_time_to_screen() every 1 second.
setInterval are also accessible in rust via
web_sys, just the names are not equal, but similar enough. Great job Rust developers!
Finally we create a string with the HTML and then inject it into
Build and done!
We have now around 50kb of wasm code. Not bad.
auto-start on startup
Right click the icon of the installed PWA and choose "Open file location" or "More" and then "Open file location". On the shortcut file
.lnk right click and choose Properties. In the tab Shortcut copy the Target. It will look something like this:
"C:\Program Files (x86)\Microsoft\Edge\Application\msedge_proxy.exe" --profile-directory=Default --app-id=cjjpkgendhneomfdeknejhpcplnagial --app-url=http://127.0.0.1:4000/rust_wasm_pwa_minimal_clock/index.html
This is the command you can use to start it from
cmd or inside the Task Scheduler.
To make it auto-start, I like to make an entry in Task Scheduler to start after my login and a couple of seconds of delay.
we need more fun
Just for fun let add a voice that reads the clock every full hour.
I used the cloud.ibm.com text-to-speech to synthesize the voice. They offer a free service for up to 10000 words in 30 days. Enough for 24 hours. After signing up and choosing the text-to-speech service, I get the api key and the url to call the service. I called it with curl and it returns and saves an ogg file.
Let's put on the screen the seconds in binary, just for fun. To show we are programmers :-)
I will never make a really beautiful icon or any graphic design. I am a programmer. But I can put some effort into it to make it a little better.
PWA needs a lot of different icon sizes. A lot. Maybe some of them are not needed or some are missing. I don't know because I copied this png files and html code from the internet. Who knows? There are some online services and CLI to make all this pngs with different sizes, but not even one is consistent. In the end I finished using paint.net or gimp and resized them manually.
Idea for the next project: make a CLI and web-service that makes all this icons, html and manifest boilerplate.
Writing PWA with Rust is fairly easy. Just do it. Use this tutorial as a minimal use case scenario.
You can then set in code any HTML you can imagine. There is no end to the possibilities.
There are many libraries to make the work with Rust and wasm even easier.