How to embed NES ROM on Itch.io

Discuss technical or other issues relating to programming the Nintendo Entertainment System, Famicom, or compatible systems. See the NESdev wiki for more information.

Moderator: Moderators

Post Reply
User avatar
Goose2k
Posts: 320
Joined: Wed May 13, 2020 8:31 am
Contact:

How to embed NES ROM on Itch.io

Post by Goose2k »

I put together a little tutorial on how to embed your NES games in browser on Itch.io. Hopefully others can get some benefit out of it!

http://www.matthughson.com/2020/07/17/n ... n-itch-io/

One thing I am not happy with is the lack of controller support. If any more experience web people want to try adding that, I would love to add it to the tutorial!

I think it looks like it just needs to be added to this file: https://github.com/bfirsh/jsnes/blob/ma ... s-embed.js.

You can see it in action here:

https://mhughson.itch.io/from-below
Attachments
itch_emu_demo.png
Last edited by Goose2k on Fri Jul 17, 2020 2:06 pm, edited 1 time in total.
User avatar
Controllerhead
Posts: 314
Joined: Tue Nov 13, 2018 4:58 am
Location: $4016
Contact:

Re: How to embed NES ROM on Itch.io

Post by Controllerhead »

Goose2k wrote: Fri Jul 17, 2020 10:32 am controller support
Looks like there is a JS gamepad API
https://developer.mozilla.org/en-US/doc ... amepad_API

Seems like you could use this to poll a controller and hook it into jsnes.Controller.BUTTON_whatever... You should take a stab at it! Maybe i'll try later tonight...
Image
User avatar
Goose2k
Posts: 320
Joined: Wed May 13, 2020 8:31 am
Contact:

Re: How to embed NES ROM on Itch.io

Post by Goose2k »

Controllerhead wrote: Fri Jul 17, 2020 1:29 pm
Goose2k wrote: Fri Jul 17, 2020 10:32 am controller support
Looks like there is a JS gamepad API
https://developer.mozilla.org/en-US/doc ... amepad_API
Yah, that's exactly what I was thinking!

If you give it a shot, let me know!!
User avatar
Controllerhead
Posts: 314
Joined: Tue Nov 13, 2018 4:58 am
Location: $4016
Contact:

Re: How to embed NES ROM on Itch.io

Post by Controllerhead »

Ok! So i got this working =)

Image

You can play it here:
http://www.nesblast.com/jsnes

Right now my demo:
- Detects a Joystick
- Switches to Joystick when detected
- All controls and axis deadzone are customizable
- Saves keyboard and joystick controls in a cookie in JSON
- Code might be somewhat legible

I used this article / demo and borrowed heavily from it:
http://beej.us/blog/data/javascript-gamepad/

Anyway, i attached the source files to this post. I would DIF them against the JSNES embed example to see what i did. Also feel free to F12 the page and go though the code / debugger, and let me know if you have any questions as far as porting it over into your tutorial, or anything really. All of the code is vanilla JS / HTML, so i hope it is readable and clear what it does. This was fun! Enjoy.
Attachments
JSNES_joystick_demo.zip
(6.26 KiB) Downloaded 131 times
Image
User avatar
Goose2k
Posts: 320
Joined: Wed May 13, 2020 8:31 am
Contact:

Re: How to embed NES ROM on Itch.io

Post by Goose2k »

Oh wow! That sounds amazing! I'm out all day today but I'll hopefully be able to try it tonight.
rox_midge
Posts: 91
Joined: Mon Sep 19, 2005 11:51 am

Re: How to embed NES ROM on Itch.io

Post by rox_midge »

It works well! Probably needs a more sophisticated configuration panel, but I was able to play Metroid (after swapping A and B) without any issues at all! With a RetroUSB and running in full screen, it's pretty damn close to the real thing; all you'd really need is Blargg's or Bisqwit's NTSC filter from there.

For accuracy, I wonder how complicated it would be to cross-compile the Mesen core to Wasm?
User avatar
Controllerhead
Posts: 314
Joined: Tue Nov 13, 2018 4:58 am
Location: $4016
Contact:

Re: How to embed NES ROM on Itch.io

Post by Controllerhead »

rox_midge wrote: Sat Jul 18, 2020 8:00 am It works well!
Not bad considering i slogged it out in one night =)
rox_midge wrote: Sat Jul 18, 2020 8:00 am Probably needs a more sophisticated configuration panel
You mean JSNES or the joystick support i added? What would you add? JSNES itself has no configuration whatsoever lol. I was thinking about deep diving into JSNES and seeing if i could contribute some features. I've built it before and already fixed the embed demo on github and that got merged. I have done some JS / Canvas stuff in the past too, so, what would you like to see? Maybe i'll fork it and goto town. I do have other projects i'm working on currently, so, it wouldn't be a priority for me, but, it might make a fun sidequest.
rox_midge wrote: Sat Jul 18, 2020 8:00 am For accuracy, I wonder how complicated it would be to cross-compile the Mesen core to Wasm?
Mesen is a truly wonderful emulator for accuracy and development; and has some incredible features. That said, it is kind of a resource hog. It also uses C, C++, C#, and a few libraries; it's complicated. I don't know the ins and outs of compiling with emscripten or however you would even go about that. If you did even get it to compile, i have some serious doubts about how well it would run in a browser... FCEUX might be a better candidate for wasm, it's much more efficient and uses more mature features of C and C++, so i'd imagine if you were crazy enough to take on such a task, i might go that route.
Image
rox_midge
Posts: 91
Joined: Mon Sep 19, 2005 11:51 am

Re: How to embed NES ROM on Itch.io

Post by rox_midge »

Controllerhead wrote: Sat Jul 18, 2020 10:08 am You mean JSNES or the joystick support i added? What would you add?
I meant the joystick support. It works 100% perfectly, but it could use some UXD, like using a diagram of the NES controller, or auto-configuring for common controllers (like the USB NES RetroPad). Basically just spit and polish; the bones are perfect :D
Controllerhead wrote: Sat Jul 18, 2020 10:08 am Mesen is a truly wonderful emulator for accuracy and development; and has some incredible features. That said, it is kind of a resource hog. It also uses C, C++, C#, and a few libraries; it's complicated. I don't know the ins and outs of compiling with emscripten or however you would even go about that. If you did even get it to compile, i have some serious doubts about how well it would run in a browser... FCEUX might be a better candidate for wasm, it's much more efficient and uses more mature features of C and C++, so i'd imagine if you were crazy enough to take on such a task, i might go that route.
Mesen's core, including the video filters, is all C++; only the GUI and some helper utilities are in C#. There's a lot even in the core library that wouldn't need to be there for a simple web-based player - Lua, 7-Zip, the debugger, HD packs... Probably it's possible to strip it down to the bare bones, compile to Wasm, connect the input to the Gamepad API, the video to Canvas, and the audio to the Web Audio API. In theory, it should run almost as fast as native in browsers that support Wasm!
User avatar
Controllerhead
Posts: 314
Joined: Tue Nov 13, 2018 4:58 am
Location: $4016
Contact:

Re: How to embed NES ROM on Itch.io

Post by Controllerhead »

rox_midge wrote: Sat Jul 18, 2020 11:30 am
Controllerhead wrote: Sat Jul 18, 2020 10:08 am You mean JSNES or the joystick support i added? What would you add?
it could use some UXD, like using a diagram of the NES controller, or auto-configuring for common controllers (like the USB NES RetroPad).
This demo, which is what it is, a demo, isn't really meant to be public facing or a finished product. I mean, there isn't an ounce of CSS on that entire page =p I also only have one joypad, an Adaptoid N64 USB controller adapter circa '99. It still works after 20 years! I don't know how i could go about adding controller support to controllers i don't have. I think taking 30 seconds to customize your controller and having it save in a cookie is way more than adequate.
rox_midge wrote: Sat Jul 18, 2020 11:30 am It works 100% perfectly ... Basically just spit and polish; the bones are perfect :D
My work here is done. That's about all i can ask for =)

I'm not really interested in pursuing this further. I only did this quick little one off thing out of curiousity and to help someone else. I'm not really a UX dude naturally. "Programmer art" is about all i can muster. I can't even draw a stick figure straight lol. One of the reasons i am targeting the NES is that it isn't art intensive visually. The cycle counting / byte conserving / 6502 ASM @ 1.7 Mhz aspect is a fun challenge. I thrive on engines and bones. And Music, i can do that =) Love me some chiptunes! Anyway, if someone else wants to jazz this thing up, be my guest! I'd be happy to help.
rox_midge wrote: Sat Jul 18, 2020 11:30 am Mesen's core, including the video filters, is all C++; only the GUI and some helper utilities are in C#. There's a lot even in the core library that wouldn't need to be there for a simple web-based player - Lua, 7-Zip, the debugger, HD packs... Probably it's possible to strip it down to the bare bones, compile to Wasm, connect the input to the Gamepad API, the video to Canvas, and the audio to the Web Audio API. In theory, it should run almost as fast as native in browsers that support Wasm!
Bruhhhhh if you can get Mesen to run on a browser that would be epic! You seem to know way more than i do about it. Clearly you are the chosen one. Go forth my son and compileth!
Image
tepples
Posts: 22705
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Re: How to embed NES ROM on Itch.io

Post by tepples »

User avatar
Controllerhead
Posts: 314
Joined: Tue Nov 13, 2018 4:58 am
Location: $4016
Contact:

Re: How to embed NES ROM on Itch.io

Post by Controllerhead »

tepples wrote: Sat Jul 18, 2020 6:13 pm em-fceux exists.
Oh wow. This is awesome!

At first glance, this appears far superior to JSNES. it runs with silky smooth graphics and audio where JSNES is a bit jittery. WebGL is so much smoother than JS Canvas. FCEUX has amazing accuracy and compatibility. Joystick seems to work. I'll have to play with it some more, and i'm not sure how cranky WebGL will be with embedding it in a page, but i think i'll be using this for any future plans personally. I did not know this existed; i'm glad i do now. Thanks for the link!

...you know, i figured FCEUX would be a good target for emscripten heh. It is!
Image
User avatar
Goose2k
Posts: 320
Joined: Wed May 13, 2020 8:31 am
Contact:

Re: How to embed NES ROM on Itch.io

Post by Goose2k »

I just gave it a go, and when I try to fun locally with the files you provided I get this error:
357850: Unable to get property 'frame' of undefined or null reference
nes-embed.js (65,3)

Code: Select all

function audio_callback(event){
	var dst = event.outputBuffer;
	var len = dst.length;
	var didFrame = false
	
	if(playAudio){
		
		// Attempt to avoid buffer underruns.
		if(audio_remain() < AUDIO_BUFFERING){ 
			nes.frame() // ISSUE IS HERE I THINK
			didFrame = true
		}
Since your zip didn't include jsnes.min.js, I had to use the version I got from git a few days ago.

Are we running against different versions of the emulator or something maybe?
User avatar
Controllerhead
Posts: 314
Joined: Tue Nov 13, 2018 4:58 am
Location: $4016
Contact:

Re: How to embed NES ROM on Itch.io

Post by Controllerhead »

Goose2k wrote: Mon Jul 20, 2020 10:06 am Are we running against different versions of the emulator or something maybe?
Oops! Maybe. I was playing with stuff. Here is my source!

Try loading this though instead of my source. It should work, i don't think i changed anything that would break it.

Code: Select all

<script type="text/javascript" src="https://unpkg.com/jsnes/dist/jsnes.min.js"></script>
EDIT: Nevermind, don't use that link. It is still the old version before i fixed the sync / high pitch sound issues. That said, that file does work on my site though with the joystick code, so, my code should work with the latest JSNES build.

Try my build, i guess, and let me know!
Attachments
src.zip
(193.42 KiB) Downloaded 126 times
Image
User avatar
Goose2k
Posts: 320
Joined: Wed May 13, 2020 8:31 am
Contact:

Re: How to embed NES ROM on Itch.io

Post by Goose2k »

Sorry, it's been a while and I ended up just implementing gamepad support myself based on this article (https://developer.mozilla.org/en-US/doc ... amepad_API).

You can find a zip of an example, used for From Below. This is what is uploaded here: https://mhughson.itch.io/from-below

Excerpt nes-embed.js:

Code: Select all

/////////////////////
// GAMEPAD SUPPORT
//
// Based on documentation here: https://developer.mozilla.org/en-US/docs/Web/API/Gamepad_API/Using_the_Gamepad_API
/////////////////////

var haveEvents = 'ongamepadconnected' in window;
var controllers = {};

// Once the presses a button on one of the controllers, this will store
// the index of that controller so that only that controller is checked
// each frame. This is to avoid additional controllers triggering key_up
// events when they are just sitting there inactive.
var cur_controller_index = -1;

function connecthandler(e) {
  addgamepad(e.gamepad);
}

function addgamepad(gamepad) {
  controllers[gamepad.index] = gamepad;
  requestAnimationFrame(updateStatus);
}

function disconnecthandler(e) {
  removegamepad(e.gamepad);
}

function removegamepad(gamepad) {
  delete controllers[gamepad.index];
}

// Check all controllers to see if player has pressed any buttons.
// If they have, store that as the current controller.
function findController()
{
	var i = 0;
	var j;

	for (j in controllers) 
	{
		var controller = controllers[j];

		for (i = 0; i < controller.buttons.length; i++) 
		{
			var val = controller.buttons[i];
			var pressed = val == 1.0;
			if (typeof(val) == "object") 
			{
				pressed = val.pressed;
				val = val.value;
			}

			if (pressed) 
			{
				cur_controller_index = j;
			}
		}
	}
}

function updateStatus() 
{
	if (!haveEvents) 
	{
		scangamepads();
	}

	// If a controller has not yet been chosen, check for one now.
	if (cur_controller_index == -1)
	{
	  findController();
	}

	// Allow for case where controller was chosen this frame
	if (cur_controller_index != -1)
	{
		var i = 0;
		var j;

		var controller = controllers[cur_controller_index];

		for (i = 0; i < controller.buttons.length; i++) 
		{
			var val = controller.buttons[i];
			var pressed = val == 1.0;
			if (typeof(val) == "object") 
			{
				pressed = val.pressed;
				val = val.value;
			}

			var player = 1 //parseInt(j,10) + 1;

			if (pressed) 
			{
				var callback = nes.buttonDown;
				switch(i)
				{
					case 12: // UP
					callback(player, jsnes.Controller.BUTTON_UP); break;
					case 13: // Down
					callback(player, jsnes.Controller.BUTTON_DOWN); break;
					case 14: // Left
					callback(player, jsnes.Controller.BUTTON_LEFT); break;
					case 15: // Right
					callback(player, jsnes.Controller.BUTTON_RIGHT); break;
					case 1: // 'A'
					callback(player, jsnes.Controller.BUTTON_A); break;
					case 0: // 'B'
					callback(player, jsnes.Controller.BUTTON_B); break;
					case 8: // Select
					callback(player, jsnes.Controller.BUTTON_SELECT); break;
					case 9: // Start
					callback(player, jsnes.Controller.BUTTON_START); break;
				}
			} 
			else 
			{
				var callback = nes.buttonUp;
				switch(i)
				{
					case 12: // UP
					callback(player, jsnes.Controller.BUTTON_UP); break;
					case 13: // Down
					callback(player, jsnes.Controller.BUTTON_DOWN); break;
					case 14: // Left
					callback(player, jsnes.Controller.BUTTON_LEFT); break;
					case 15: // Right
					callback(player, jsnes.Controller.BUTTON_RIGHT); break;
					case 1: // 'A'
					callback(player, jsnes.Controller.BUTTON_A); break;
					case 0: // 'B'
					callback(player, jsnes.Controller.BUTTON_B); break;
					case 8: // Select
					callback(player, jsnes.Controller.BUTTON_SELECT); break;
					case 9: // Start
					callback(player, jsnes.Controller.BUTTON_START); break;
				}
			}

			// var axes = d.getElementsByClassName("axis");
			// for (i = 0; i < controller.axes.length; i++)
			// {
				// var a = axes[i];
				// a.innerHTML = i + ": " + controller.axes[i].toFixed(4);
				// a.setAttribute("value", controller.axes[i] + 1);
			// }
		}
	}
		
	requestAnimationFrame(updateStatus);
}

function scangamepads() {
  var gamepads = navigator.getGamepads ? navigator.getGamepads() : (navigator.webkitGetGamepads ? navigator.webkitGetGamepads() : []);
  for (var i = 0; i < gamepads.length; i++) {
    if (gamepads[i]) {
      if (gamepads[i].index in controllers) {
        controllers[gamepads[i].index] = gamepads[i];
      } else {
        addgamepad(gamepads[i]);
      }
    }
  }
}

window.addEventListener("gamepadconnected", connecthandler);
window.addEventListener("gamepaddisconnected", disconnecthandler);

if (!haveEvents) {
 setInterval(scangamepads, 500);
}
Attachments
from_below_2020_09_16_v_1_0_0.zip
(21.68 KiB) Downloaded 67 times
User avatar
Controllerhead
Posts: 314
Joined: Tue Nov 13, 2018 4:58 am
Location: $4016
Contact:

Re: How to embed NES ROM on Itch.io

Post by Controllerhead »

Well i'm glad you got something you're happy with up and running!

I did take into account having an analog input for movement, and implemented a (crude) axis deadzone slider. You may want to consider that. If i remember correctly, the API returns a number between 0 and 1.
Image
Post Reply