Skip to content

Commit 0dbd00f

Browse files
authored
Created an Embeddable Iframe Version of WasmBoy (#320)
* Got the iframe setup, and building with dev enviornment * Updated deps * Got a working iframe build system! Even has live reloading for once haha! * Finished up the WasmBoy Player, and the play poster. Back to where I was on the plane :p * Mobile Debugger, WasmBoy Embed * Changed Mobile Debugger Open Icon * Created the WasmBoy Embed Demo Controls Bar * Got Save States Working * Added a modal system, and load state feature * Finished the embed demo! * Added analytics to Iframe Embed * Added docs for iframe * Commenting out wasmerboy builds for now
1 parent 9c5cfc0 commit 0dbd00f

26 files changed

+1500
-17
lines changed

.travis.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,4 +11,4 @@ script:
1111
- npm run demo:build:apps
1212
- npm run test:accuracy:nobuild
1313
- npm run test:integration:nobuild
14-
- npm run wasmerboy:build
14+
#TODO: - npm run wasmerboy:build

README.md

Lines changed: 41 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111

1212
<!--- Short Description-->
1313

14-
🎮👾🕹️ Gameboy Emulator Library written in Web Assembly using [AssemblyScript](https://github.com/AssemblyScript/assemblyscript), Debugger/Shell in Preact 🎮👾🕹️
14+
🎮👾🕹️ Gameboy Emulator Library written in Web Assembly using [AssemblyScript](https://github.com/AssemblyScript/assemblyscript), Demos in [Preact](https://preactjs.com/) and [Svelte](https://svelte.dev/) 🎮👾🕹️
1515

1616
**Project is still < 1.0.0. Most games are playable, but the emulator is still not very accurate. Expect bugs.**
1717

@@ -36,6 +36,7 @@
3636
- [Demo Applications](#demo-applications)
3737
- [Debugger](#debugger)
3838
- [Benchmark](#benchmark)
39+
- [Iframe Embed](#iframe-embed)
3940
- [Tests](#tests)
4041
- [Blarrg](#blarrg)
4142
- [Mooneye](#mooneye)
@@ -109,6 +110,12 @@ A full debugger meant for analyzing the internals of the gameboy. Great for Home
109110
- Saved Layouts between sessions. 💠
110111
- Help widget with tips on how to be effective in the debugger. 🙋
111112

113+
**Mobile Demo**
114+
115+
For UI/UX reasons, on mobile the debugger is simply a web app for testing the lib. This is useful for testing a ROM on the go. For playing games, I would suggest [VaporBoy](https://vaporboy.net/). Below is an example of the mobile demo:
116+
117+
![Pokemon Crystal Wasmboy Mobile Demo](./docs/images/debuggerMobileDemo.gif)
118+
112119
**Anaytics / Privacy**
113120

114121
[Analytics Wrapper Service](./demo/debugger/analytics.js)
@@ -123,12 +130,6 @@ Analytics is used on this application simply for performance monitoring, and tra
123130
- Whenever the Google Drive option is selected.
124131
- Whenever the mobile demo is manually reloaded.
125132

126-
**Mobile Demo**
127-
128-
For UI/UX reasons, on mobile the debugger is simply a web app for testing the lib. This is useful for testing a ROM on the go. For playing games, I would suggest [VaporBoy](https://vaporboy.net/). Below is an example of the mobile demo:
129-
130-
![Pokemon Crystal Wasmboy Mobile Demo](./docs/images/debuggerMobileDemo.gif)
131-
132133
### Benchmark
133134

134135
[Application Link](https://wasmboy.app/benchmark/)
@@ -137,6 +138,10 @@ For UI/UX reasons, on mobile the debugger is simply a web app for testing the li
137138

138139
Since WasmBoy is built in AssemblyScript, it can also run it's core through the Typescript compiler if we mock out some of the WebAssembly interface. The benchmarking tool was built as a way to compare WebAssembly performance to Javascript / ES6 performance, after compiling the core to both WebAssembly and Javascript. It includes detailed stats, live running output, and multiple graphs. Also great for comparing the performance of devices that run WasmBoy.
139140

141+
**Example**
142+
143+
![WasmBoy Benchmark Runner Section on Safari](./docs/images/benchmarkSafariBackToColorRunner.png)
144+
140145
**Anaytics / Privacy**
141146

142147
Analytics is used on this application simply for performance monitoring, and tracking popularity of the application. The following events are sent, with nothing more than the event name. The analytics provider used is [Google Analytics](https://analytics.google.com/analytics/web/).
@@ -145,9 +150,35 @@ Analytics is used on this application simply for performance monitoring, and tra
145150
- Whenever the benchmark is ran.
146151
- Whenever results are rendered for the benchmark.
147152

153+
### Iframe Embed
154+
155+
An Iframe embeddable version of WasmBoy. Simply provide information through [URL Query Params](https://en.wikipedia.org/wiki/Query_string), and embed a ROM on your website! Great for embedding your HomeBrew Game Boy / Game Boy Color games on your website, (WordPress) blog, and game hosting services such as [itch.io](https://itch.io/).
156+
148157
**Example**
149158

150-
![WasmBoy Benchmark Runner Section on Safari](./docs/images/benchmarkSafariBackToColorRunner.png)
159+
[Example Tobu Tobu Girl, Homebrew Hub Iframe](https://wasmboy.app/iframe/?rom-name=Tobu%20Tobu%20Girl&play-poster=https://gbhh.avivace.com/database/entries/tobutobugirl/screenshot1.bmp&rom-url=https://gbhh.avivace.com/database/entries/tobutobugirl/tobu.gb)
160+
161+
![Gif of the Tobu Tobu Girl, Home brew hub example](./docs/images/iframeEmbed.gif)
162+
163+
**Usage**
164+
165+
Add an iframe to your website like the following:
166+
167+
```html
168+
<iframe title="WasmBoy Iframe Embed" width="160" height="144" src="https://wasmboy.app/iframe/?[QUERY_PARAMS_GO_HERE]"> </iframe>
169+
```
170+
171+
The iframe is configured by adding [URL Query Params](https://en.wikipedia.org/wiki/Query_string). The configuration params are:
172+
173+
- `rom-url` - **(Required)** The URL to the `.gb` or `.gbc` ROM that will be loaded, fetched, and played.
174+
- `rom-name` - The name of the ROM being played.
175+
- `play-poster` - The URL to the image shown at the intial "click to play", play poster.
176+
177+
Please ensure all assets that are being loaded by the iframe embed, such as ROMs and images, will work with [CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS). The WasmBoy Iframe Embed will take the full width and height (100%) of it's iframe container. Thus, it will be up to your styling to ensure the iframe preserves the GameBoy 160x144 resolution.
178+
179+
**Anaytics / Privacy**
180+
181+
Analytics is used on this application simply for performance monitoring, and tracking popularity of the application. The analytics provider used is [Google Analytics](https://analytics.google.com/analytics/web/). Only basic user visit data is recorded / used.
151182

152183
# Tests
153184

@@ -325,3 +356,5 @@ Using the [gh-pages](https://www.npmjs.com/package/gh-pages) for debugger/demo d
325356
- [The Cycle Accurate Game Boy Docs](https://github.com/AntonioND/giibiiadvance/blob/master/docs/TCAGBD.pdf)
326357

327358
- [Demos for perf testing GB/GBC](http://privat.bahnhof.se/wb800787/gb/demos/)
359+
360+
- [Homebrew Hub - Collection of GameBoy Homebrew](https://gbhh.avivace.com/)

demo/debugger/components/mobile/mobile.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ export default class Mobile extends Component {
8484
{/* Getting Started */}
8585
<h3>Getting Started</h3>
8686
<div>
87-
To try a ROM / Game, click the 💾 button, to open the ROM loader. To Play/Pause emulation, click the ⏯️ button. For more
87+
To try a ROM / Game, click the 📁 button, to open the ROM loader. To Play/Pause emulation, click the ⏯️ button. For more
8888
information on WasmBoy, click the ℹ️ button. To reload this demo, click the ♻️ button.
8989
</div>
9090
</div>

demo/debugger/components/mobile/touchpad/touchpad.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ export default class Touchpad extends Component {
144144

145145
togglePlayPause() {
146146
if (!WasmBoy.isLoadedAndStarted()) {
147-
Pubx.get(PUBX_KEYS.NOTIFICATION).showNotification('Please load a ROM first. 💾');
147+
Pubx.get(PUBX_KEYS.NOTIFICATION).showNotification('Please load a ROM first. 📁');
148148
return;
149149
}
150150

@@ -185,7 +185,7 @@ export default class Touchpad extends Component {
185185

186186
<div class="debugger-input">
187187
<button class="remove-default-button" onClick={() => this.openROM()}>
188-
💾
188+
📁
189189
</button>
190190
<button class="remove-default-button" onClick={() => this.togglePlayPause()}>
191191
⏯️

demo/iframe/App.svelte

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
<script>
2+
import PlayPoster from './components/PlayPoster.svelte';
3+
import WasmBoy from './components/WasmBoy.svelte';
4+
import Modal from './components/Modal.svelte';
5+
import ControlsBar from './components/ControlsBar.svelte';
6+
import {isStarted, isLoaded} from './stores.js';
7+
8+
import loadScript from 'load-script';
9+
10+
// Load our analytics
11+
if (typeof window !== 'undefined') {
12+
loadScript('https://www.googletagmanager.com/gtag/js?id=UA-125276735-3', (err, script) => {
13+
if (err) {
14+
console.error(err);
15+
return;
16+
}
17+
18+
window.dataLayer = window.dataLayer || [];
19+
function gtag() {
20+
window.dataLayer.push(arguments);
21+
}
22+
gtag('js', new Date());
23+
gtag('config', 'UA-125276735-3');
24+
});
25+
}
26+
</script>
27+
28+
<main class="app">
29+
{#if $isStarted === false}
30+
<PlayPoster />
31+
{:else}
32+
<WasmBoy />
33+
{#if $isLoaded}
34+
<Modal />
35+
<ControlsBar />
36+
{/if}
37+
{/if}
38+
</main>
39+
40+
<style>
41+
.app {
42+
width: 100%;
43+
height: 100%;
44+
}
45+
46+
:global(.icon-button) {
47+
width: 50px;
48+
height: 50px;
49+
background-color: transparent;
50+
border: none;
51+
cursor: pointer;
52+
}
53+
54+
/* https://www.30secondsofcode.org/css/s/donut-spinner/ */
55+
@keyframes donut-spin {
56+
0% {
57+
transform: rotate(0deg);
58+
}
59+
100% {
60+
transform: rotate(360deg);
61+
}
62+
}
63+
64+
:global(.donut) {
65+
display: inline-block;
66+
border: 4px solid rgba(0, 0, 0, 0.1);
67+
border-left-color: #654ff0;
68+
border-radius: 50%;
69+
width: 50px;
70+
height: 50px;
71+
animation: donut-spin 1.2s linear infinite;
72+
}
73+
74+
:global(.error) {
75+
color: red;
76+
}
77+
</style>
72.3 KB
Loading

demo/iframe/components/About.svelte

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
<script>
2+
import {playPoster, romName, romUrl} from '../stores.js';
3+
import packageJson from "../../../package.json";
4+
</script>
5+
6+
<div class="about">
7+
8+
<h1>WasmBoy Embed Player</h1>
9+
<p>This is a dynamic GameBoy / GameBoy color ROM player powered by WasmBoy. WasmBoy is a Game Boy / Game Boy Color emulation library, 🎮written for WebAssembly using <a href="https://github.com/AssemblyScript/assemblyscript" target="_blank">AssemblyScript</a>. 🚀 This embed is built with <a href="https://svelte.dev/" target="_blank">Svelte</a>, and input is handled by <a href="https://github.com/torch2424/responsive-gamepad" target="_blank">Responsive Gamepad</a>. WasmBoy is written by <a href="https://github.com/torch2424" target="_blank">Aaron Turner (torch2424)</a>, and Licensed under <a href="https://github.com/torch2424/wasmboy/blob/master/LICENSE" target="_blank">GPL 3.0</a>.</p>
10+
<div>WasmBoy Version: {packageJson.version}</div>
11+
<h1>WasmBoy Links</h1>
12+
<ul>
13+
<li><a href="https://github.com/torch2424/wasmboy" target="_blank">Github Repo</a></li>
14+
<li><a href="https://www.npmjs.com/package/wasmboy" target="_blank">NPM Package</a></li>
15+
</ul>
16+
<h1>Embed Configuration</h1>
17+
<ul>
18+
{#if $romName}
19+
<li><b>ROM Name:</b> {$romName}</li>
20+
{/if}
21+
{#if $romUrl}
22+
<li><b>ROM URL:</b> <a href={$romUrl} target="_blank">{$romUrl}</a></li>
23+
{/if}
24+
{#if $playPoster}
25+
<li><b>Play Poster:</b> <a href={$playPoster} target="_blank">{$playPoster}</a></li>
26+
{/if}
27+
</ul>
28+
<h1>Help</h1>
29+
<h2>Controls</h2>
30+
<p>This player supports Keyboard and Gamepad input per the default <a href="https://github.com/torch2424/responsive-gamepad" target="_blank">Responsive Gamepad</a> configuration. For keyboard controls: Directional (Dpad) controls are handled by the arrows keys, or WASD on your keyboard. The A button is controlled by the "Z" key. The B button is controlled by the "X" key. Start is handled by the "Enter" key. Select is handled by the "Shift" key.</p>
31+
<h2>Saves States / Loading States</h2>
32+
<p>Save states can be made by clicking the save icon in the control bar at the bottom of the page. Save states will also be made whenever you navigate away from the running iframe, or when the page is refreshed. Save states can be loaded by clicking the load icon in the control bar, and choosing the appropiate save state represented by the screenshot, and time at which the save states was made. Save states are stored locally in your browser in the IndexedDB API. Meaning they are not backed up to a server, and can be accidentally deleted when clearing your browser cache.</p>
33+
<h2>Reporting Bugs / Making Suggestions</h2>
34+
<p>Please feel free to file any bugs, suggestions, issues, etc.. At the <a href="https://github.com/torch2424/wasmboy" target="_blank">WasmBoy Github repo</a>.</p>
35+
</div>
36+
37+
38+
<style>
39+
.about {
40+
color: #fff;
41+
font-size: 1.15em;
42+
}
43+
44+
.about a, .about a:visited {
45+
color: #b5cbfc;
46+
}
47+
</style>
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
<script>
2+
import {isPlaying, setStatus, status, triggerSaveState, showLoadState, showAbout} from '../stores.js';
3+
import PlayIcon from './icons/PlayIcon.svelte';
4+
import PauseIcon from './icons/PauseIcon.svelte';
5+
import SaveIcon from './icons/SaveIcon.svelte';
6+
import LoadIcon from './icons/LoadIcon.svelte';
7+
import AboutIcon from './icons/AboutIcon.svelte';
8+
9+
// Subscribe to our current status
10+
let displayStatus = false;
11+
let statusTimeout;
12+
// Subscribe to status changes, and show the status message
13+
// on changes
14+
status.subscribe(value => {
15+
16+
if (statusTimeout) {
17+
clearTimeout(statusTimeout);
18+
}
19+
20+
displayStatus = true;
21+
22+
if (value.timeout < 0) {
23+
return false;
24+
}
25+
26+
statusTimeout = setTimeout(() => {
27+
displayStatus = false;
28+
statusTimeout = undefined;
29+
}, value.timeout)
30+
});
31+
32+
const handlePlayPause = () => {
33+
if ($isPlaying) {
34+
isPlaying.set(false);
35+
setStatus('Paused', -1);
36+
} else {
37+
isPlaying.set(true);
38+
setStatus('Playing!');
39+
}
40+
};
41+
42+
const handleSave = () => {
43+
triggerSaveState();
44+
};
45+
46+
const handleLoad = () => {
47+
showLoadState();
48+
};
49+
50+
const handleAbout = () => {
51+
showAbout();
52+
};
53+
</script>
54+
55+
<footer class="controls-bar">
56+
{#if displayStatus}
57+
<div class="status">{$status.message}</div>
58+
{/if}
59+
60+
<ul class="controls-buttons">
61+
<li>
62+
<button class="icon-button" on:click={handlePlayPause}>
63+
{#if $isPlaying}
64+
<PauseIcon />
65+
{:else}
66+
<PlayIcon />
67+
{/if}
68+
</button>
69+
</li>
70+
71+
<li>
72+
<button class="icon-button" on:click={handleSave}>
73+
<SaveIcon />
74+
</button>
75+
</li>
76+
77+
<li>
78+
<button class="icon-button" on:click={handleLoad}>
79+
<LoadIcon />
80+
</button>
81+
</li>
82+
83+
<li>
84+
<button class="icon-button" on:click={handleAbout}>
85+
<AboutIcon />
86+
</button>
87+
</li>
88+
</ul>
89+
</footer>
90+
91+
<style>
92+
93+
.controls-bar {
94+
position: fixed;
95+
bottom: 0;
96+
right: 0;
97+
width: 100%;
98+
height: 100%;
99+
100+
transform: translateY(0px);
101+
transition: transform 0.5s;
102+
}
103+
104+
:global(html):hover .controls-bar, :global(body):hover .controls-bar {
105+
transform: translateY(-50px);
106+
}
107+
108+
.status, .controls-buttons {
109+
position: absolute;
110+
bottom: 0;
111+
right: 0;
112+
background-color: rgba(0,0,0,0.85);
113+
}
114+
115+
.status {
116+
display: flex;
117+
justify-content: center;
118+
align-items: center;
119+
text-align: center;
120+
121+
padding: 20px;
122+
123+
color: #fff;
124+
125+
z-index: 1;
126+
}
127+
128+
.controls-buttons {
129+
display: flex;
130+
justify-content: space-around;
131+
align-items: center;
132+
133+
list-style-type: none;
134+
135+
transform: translateY(50px);
136+
height: 50px;
137+
width: 100%;
138+
margin: 0px;
139+
padding: 0px;
140+
}
141+
</style>

0 commit comments

Comments
 (0)