Very cool! I recently worked on a similar project (which is not quite ready to launch), and also had a lot of trouble with the interface. It seems like your Gameboy emulator runs way too fast on my machine; is the framerate not actually locked to the system clock?
I had JavaScript driving the UI interaction in my project, so to deal with locking, I let the browser control the framerate. Every requestAnimationFrame event, I compared the current time to the last frame I had drawn, and if it was time for the next, only then performed the logic to simulate the next frame. I still targeted 60 FPS, but this allowed the emulator to lag behind if the browser couldn't keep up, which seems to be a necessary compromise to maintain UI responsiveness.
You might give the requestAnimationFrame event model a try, and see if it helps with your keyboard issues. I remember having similar problems if I tried to let WASM drive the simulation directly, especially on weaker hardware.
Either way, that's nit picking all around. This is impressive stuff, and I hope you keep working on it!
Hey! Yeah there are bugs in the emulator so it's not time perfect, I've not really touched the emulator code in about 5 years so will take me some time to get back into it.
Thanks for the tip about requestAnimationFrame (/u/knackers mentioned as well) I'll look into it! That's what's good about sharing this stuff - you learn something new!
I'll probably tinker with the project here and there, I want to implement another cartridge type that for some reason I left out, I found a ROM last night that wasn't supported and I think it's MBC2. But I don't intend for it to be a perfect emulator or anything, more just a sandbox to play around with. There are definitely better emulators out there that strive for accuracy.
Btw, my WebAssembly emulators here (https://floooh.github.io/tiny8bit/) work quite well with a 60hz frame update even on mobile devices (some of them are still quite CPU hungry since they're doing a cycle-correct display emulation).
The WebAssembly version uses the following HTML5 features:
- requestAnimationFrame for frame scheduling
- WebGL for rendering (emulator code creates a linear RGBA8 framebuffer, which is copied into a double-buffered WebGL texture, which is then rendered as a fullscreen rectangle)
- WebAudio with ScriptProcessorNode and 2048-samples output buffer size to 'render' the audio (at 44.1 kHz), technically ScriptProcessorNode is deprecated, it works surprisingly well though
- everything is running in the browser thread
- everything is written in C, no dynamic memory allocation, except at startup
Thanks for this. It's pleasing to hear there might be a better way!
I shot myself in the foot a bit when I saw the web worker thing working (ish!) and carried on down that path, so didn't really explore the other means with requestAnimationFrame etc
Will be definitely looking at it now though, it's good to know that everything can run on the browser thread!
Nice writeup, thanks. I'd been thinking of playing with Go/WASM for http://8bitworkshop.com someday. For now, JS is plenty fast enough for 1-3 MHz 8-bit emulators.
BTW, I've never had performance problems with HTML Canvas, and I use setTimeout(), not requestAnimationFrame. The only thing I do every frame is putImageData() on a retained Uint32Array buffer. Maybe you could use the Chrome Performance devtools to figure out what is going on.
Very cool. I suspect running on the main thread with requestAnimationFrame for rendering to the canvas would eliminate some of the performance issues without having to rely on postmessage.
I had JavaScript driving the UI interaction in my project, so to deal with locking, I let the browser control the framerate. Every requestAnimationFrame event, I compared the current time to the last frame I had drawn, and if it was time for the next, only then performed the logic to simulate the next frame. I still targeted 60 FPS, but this allowed the emulator to lag behind if the browser couldn't keep up, which seems to be a necessary compromise to maintain UI responsiveness.
You might give the requestAnimationFrame event model a try, and see if it helps with your keyboard issues. I remember having similar problems if I tried to let WASM drive the simulation directly, especially on weaker hardware.
Either way, that's nit picking all around. This is impressive stuff, and I hope you keep working on it!