Table of Contents
Introduction
Name: Dither-gui
Description: WASM powered web GUI for my dithering code
Repository: Currently not public
Public Link: this link is not final
Motivation
After finishing my simple dithering project, I really wanted a GUI for it. However, I also really wanted it to have some really random and arbitrary requirements:
- Ran fully on the user's device. I don't want to be paying for processing.
- Hosted on Github Pages. Again, I don't want to pay for server hosting.
- As cross-platform as possible. I want this to run on as many devices as possible.
I looked around and I decided to try out egui. It was pretty simple why:
- WASM support with eframe
- Relatively simple way of defining the interface
- Felt snappy in the demo
- PWA support with
eframe
That was that, I chose egui
.
Development
egui
egui
is an intermediate mode
GUI framework, which means it updates the entire screen every frame.
This proved to be a challenge as I had to rethink how to approach the logic for this application.
I ended up basically implementing a state machine, which kept track of when it needed to process the image, and when to re-render it to the screen.
dithering
The dithering logic is basically the same as ditherpunk
wasm
Compiling egui
/eframe
for WASM was tricky. I ran into a ton of fringe compatibility issues as I slowly added more libraries. In particular,
I had a lot of issues with trying to find the right async
runtime for WASM.
Why did I need async
? Well, I wanted the image processing to happen in the background, while allowing the GUI to still be responsive.
In a normal application, you'd just .await()
the Future<T,F>
and you'd have T
to use at some point. Simple!
But the problem that I ran into was that almost NONE of the WASM compatible async runtimes would return T
! I even ran into a few weird cases where rust-analyzer would
be able to deduce what should be returned, but the compiler would still tell me that it's getting the unit type ()
! This proved to be a huge issue, because without
being able to get T
from a Future
, I wasn't sure how I'd be able to realistically achieve what I wanted.
Thankfully, after tons of googling and rading github issues, I found poll_promise
. This was the only async runtime I could
find that both supported WASM, and would return T
from Promise<T>
.
saving
After generating your dithered image, chances are you'd want to save it. This is when I ran into another issue, how do I "save" in a WASM environment?
Turns out there's no easy way for WASM to interact with the underlying system's file dialog. rfd
is the package I chose to allow users to select files, and thankfully they
also have a super super barebones implementation of "Saving" on WASM. All it does is provide a link to the image, allowing users to save it. While that's not the neatest solution,
I think it's good enough.
Retrospectives
While I achieved my goal of having this application be WASM based and run fully on the user's device, I am not satisfied with it on mobile. egui
/eframe
does not scale well
on mobile. However, I'm not sure if that's just a limitation of the crates, or if I just didn't do what's required for proper mobile support.