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.