Homebrew [Preview] New 3DS Web Browser web app

  • Thread starter jsa
  • Start date
  • Views 8,285
  • Replies 20
  • Likes 5

jsa

Well-Known Member
OP
Member
Joined
Oct 21, 2015
Messages
224
Trophies
0
Location
Devon, UK
Website
muffinti.me
XP
396
Country
United Kingdom
UPDATE: Bookmark this page - http://jsa.x10host.com/newTab.php - instead of the actual thing - it does the stuff needed (open in a clean tab) to make it work properly

Also REMOVED the symbols (letters/arrows) on A/B/X/Y buttons and the D-Pad, makes it look clean and modern. Buttons become coloured on-screen when pressed! (actually that was done before) A/B/X/Y match the colours of the buttons on a standard N3DS (which is also the colour of the letters on the buttons on a N3DSXL which is less rainbow-y)

TL;DR: A tech demo for using almost all of the New 3DS's hardware buttons/sticks via the browser.

--Just realized I forgot to add Start and Select. Will update asap.--

So, seeing as I can't get on my computer that much to write C homebrew for the 3DS, I figured I'd make some web-based homebrew instead.

I actually wrote all 300 lines of code with the New 3DS browser as my family tend occupy the laptop a lot. (That's why HomebreWWW/Koopa Cruiser development is slow, guys!)

Basically, what this is is a HTML5/JavaScript web app which, when opened, shows you what buttons you may be pressing (including Circle Pad/C-Stick position) using the HTML5 Gamepad API. (nb, not related to the Wii U gamepad though I believe the Wii U supports that API too).

The code's a bit of a mess (what did you expect, it being written entirely with the New 3DS) but it works.

I hope to clean this up a bit in future and release a framework/library for making web-based games for the New 3DS, once I've got Koopa Cruiser/HomebreWWW in a usable state.

This supports reading the states of all the buttons on the New 3DS barring the HOME button. Please note that ZL and ZR are practically unusable (they change the tab) and B cannot be held down for more than 500ms (half a second) without the tab being closed (it's the shortcut to close the tab). Though popping up a JavaScript alert stops the tab closing, "hold B" is not usable. L and R are usable providing the page is opened via the "Open in New Tab" option when you tap and hold on a link. This is because if there are items in the tab's history it will go back and forward, therefore it needs to be programmatically opened in a tab, or via the context menu (for lack of a better word). I will make a small poster page to do this automatically.

Also, Start and Select are mapped to the browser menu and bookmarks/history respectively, so are unusable.

All other buttons are usable: D-pad, Circle Pad, C-Stick, A, B (partial), X and Y.

Also, though I haven't yet implemented it, you can read the touch screen position, a really simple thing to do if you spend 10 minutes with Google.

Please note this is only for the New Nintendo 3DS browser and consequently will NOT work with older DS and 3DSes, though it may work with the Wii U and possibly a game controller connected to a PC. ;)

http://jsa.x10host.com/n3ds_gamepad.html (tap and hold then select "Open in New Tab")
 
Last edited by jsa,

TamDanny

GBATemp 3DS Fanatic
Member
Joined
Aug 20, 2015
Messages
315
Trophies
0
XP
457
Country
Mexico
Wow. I tested this out on my system, and it works pretty well! Pretty impressive that you typed this all in the 3DS. :)
 

jsa

Well-Known Member
OP
Member
Joined
Oct 21, 2015
Messages
224
Trophies
0
Location
Devon, UK
Website
muffinti.me
XP
396
Country
United Kingdom
nice!!! i'm keeping this bookmarked just in case I need to test my buttons :yay3ds:
yeah you have a slight issue there as you need to open it in a "clean" tab for the buttons to work. I made a splash page that basically has JavaScript that will open the tab "properly" (window.open)

http://jsa.x10host.com/newTab.php

tl;dr: bookmark that link to make everything work properly when launching it from bookmarks
 

jsa

Well-Known Member
OP
Member
Joined
Oct 21, 2015
Messages
224
Trophies
0
Location
Devon, UK
Website
muffinti.me
XP
396
Country
United Kingdom
@jsa do you think it's possible to use the touch screen. The 3DS does not support Touch Events API. Maybe another technic might work? What do you think?
Actually, the New 3DS does support the Touch Events API! :D
For the Old 3DS, however, you could use mouseDown events and such. (I believe it is window.onMouseDown)

By the way, every HTML5 standard API supported on the Wii U browser will work on the New 3DS browser. Their HTML5 te
 

Crayon2000

Well-Known Member
Member
Joined
Feb 4, 2013
Messages
131
Trophies
1
XP
738
Country
Canada
Actually, the New 3DS does support the Touch Events API! :D
I thought it was not supported because it's not on their list of Web Standards:
  • HTML 4.01
  • HTML 5
  • XHTML 1.1
  • Fullscreen API
  • Gamepad API
  • SVG
  • WebSocket
  • Video Subtitle
  • WOFF
  • Web Messaging
  • Server-Sent Events
  • Web Storage (elements used)
  • XMLHttpRequest
  • Canvas
  • Video
  • DOM Levels 1-3
  • ECMAScript
  • CSS 1
  • CSS 2.1
  • CSS 3 (elements used)
By the way, every HTML5 standard API supported on the Wii U browser will work on the New 3DS browser.

The Wii U support Device Orientation, so you think it's available on the New 3DS?

For the Wii U Gamepad I mostly use their custom API.
 

jsa

Well-Known Member
OP
Member
Joined
Oct 21, 2015
Messages
224
Trophies
0
Location
Devon, UK
Website
muffinti.me
XP
396
Country
United Kingdom
I thought it was not supported because it's not on their list of Web Standards:
  • HTML 4.01
  • HTML 5
  • XHTML 1.1
  • Fullscreen API
  • Gamepad API
  • SVG
  • WebSocket
  • Video Subtitle
  • WOFF
  • Web Messaging
  • Server-Sent Events
  • Web Storage (elements used)
  • XMLHttpRequest
  • Canvas
  • Video
  • DOM Levels 1-3
  • ECMAScript
  • CSS 1
  • CSS 2.1
  • CSS 3 (elements used)


The Wii U support Device Orientation, so you think it's available on the New 3DS?

For the Wii U Gamepad I mostly use their custom API.

I tried the Device Orientation API, but it is non functional. It's just locked in a single position.
 

Crayon2000

Well-Known Member
Member
Joined
Feb 4, 2013
Messages
131
Trophies
1
XP
738
Country
Canada
I've also made a demo for Touch Events, http://jsa.x10host.com/n3ds/ (<- the new landing page for the N3DS HTML5 demos)
I have just tested on the Wii U Gamepad and it seems to work. I'm going to check the documentation and try to be familiar with this new API.
See if it and/or the buttond test work on Wii U
The Gamepad API does not seem to Work with the Wii U.
 

jsa

Well-Known Member
OP
Member
Joined
Oct 21, 2015
Messages
224
Trophies
0
Location
Devon, UK
Website
muffinti.me
XP
396
Country
United Kingdom
I have just tested on the Wii U Gamepad and it seems to work. I'm going to check the documentation and try to be familiar with this new API.

The Gamepad API does not seem to Work with the Wii U.
Perhaps we could create a kind of abstraction layer which translates wiiu.* to standard HTML5 APIs.
 

Crayon2000

Well-Known Member
Member
Joined
Feb 4, 2013
Messages
131
Trophies
1
XP
738
Country
Canada
Perhaps we could create a kind of abstraction layer which translates wiiu.* to standard HTML5 APIs.
Actually I have the exact opposite :)

Here is my code for the untested beta 4 with touch support:
HTML:
<!doctype html>
<html>
    <head>
        <meta http-equiv="content-type" content="text/html; charset=utf-8" />
        <meta name="viewport" content="width=400 user-scalable=no" />
        <link rel="shortcut icon" href="favicon.ico" type="image/vnd.microsoft.icon" />
        <link rel="stylesheet" type="text/css" href="bootstrap/css/bootstrap.min.css" />
        <link rel="stylesheet" type="text/css" href="bootstrap/css/bootstrap-theme.min.css">
        <link rel="stylesheet" type="text/css" href="usendmii.css" />
        <title>UsendMii</title>
        <script type="text/javascript">
            const STICK_PRESSED = 0.06;
            var MySocket = new WebSocket("ws://" + window.location.host);
            var interval;
            var tpTouch = 0, tpX = 0, tpY = 0;

            MySocket.onopen = function()
            {
                document.getElementById("out").innerHTML = "Connected";

                var el = document.getElementsByTagName("canvas")[0];
                el.addEventListener("touchmove", handleMove, false);
                el.addEventListener("touchstart", handleStart, false);
                el.addEventListener("touchend", handleEnd, false);

                setInterval('update()', 10);
            };

            MySocket.onmessage = function(evt)
            {
                document.getElementById("out").innerHTML = evt.data;
            };

            MySocket.onclose = function()
            {
                document.getElementById("out").innerHTML = "Disconnected";
                clearInterval(interval);
            };

            function handleMove(evt) {
                evt.preventDefault();
                var touch = evt.changedTouches[0];
                tpX = touch.pageX;
                tpY = touch.pageY;
            }

            function handleStart(evt) {
                evt.preventDefault();
                tpTouch = 1;
            }

            function handleEnd(evt) {
                evt.preventDefault();
                tpTouch = 0;
            }

            function update()
            {
                if(MySocket.bufferedAmount > 0) {
                    return; // There is data in the queue
                }

                var gp = navigator.webkitGetGamepads()[0];
                var gamepadState = {tpTouch:0, tpX:0, tpY:0, hold:0, lStickX:0, lStickY:0, rStickX:0, rStickY:0};
                var hold = 0;

                if(gp.buttons[0] == 1) {
                    hold |= 0X00004000; // B
                }
                if(gp.buttons[1] == 1) {
                    hold |= 0X00008000; // A
                }
                if(gp.buttons[2] == 1) {
                    hold |= 0X00001000; // Y
                }
                if(gp.buttons[3] == 1) {
                    hold |= 0X00002000; // X
                }
                if(gp.buttons[4] == 1) {
                    hold |= 0X00000020; // L
                }
                if(gp.buttons[5] == 1) {
                    hold |= 0X00000010; // R
                }
                if(gp.buttons[6] == 1) {
                    hold |= 0X00000080; // ZL
                }
                if(gp.buttons[7] == 1) {
                    hold |= 0X00000040; // ZR
                }
                if(gp.buttons[8] == 1) {
                    hold |= 0X00000008; // Start
                }
                if(gp.buttons[9] == 1) {
                    hold |= 0X00000004; // Select
                }
                if(gp.buttons[10] == 1) {
                    hold |= 0X00040000; // Circle Pad Click
                }
                if(gp.buttons[11] == 1) {
                    hold |= 0X00020000; // C Stick Click
                }
                if(gp.buttons[12] == 1) {
                    hold |= 0X00000200; // Up
                }
                if(gp.buttons[13] == 1) {
                    hold |= 0X00000100; // Down
                }
                if(gp.buttons[14] == 1) {
                    hold |= 0X00000800; // Left
                }
                if(gp.buttons[15] == 1) {
                    hold |= 0X00000400; // Right
                }
                if(gp.axes[0] < -STICK_PRESSED) {
                    hold |= 0X40000000; // Circle Pad Left
                }
                else if(gp.axes[0] > STICK_PRESSED) {
                    hold |= 0X20000000; // Circle Pad Right
                }
                if(gp.axes[1] < -STICK_PRESSED) {
                    hold |= 0X10000000; // Circle Pad Up
                }
                else if(gp.axes[1] > STICK_PRESSED) {
                    hold |= 0X08000000; // Circle Pad Down
                }
                if(gp.axes[2] < -STICK_PRESSED) {
                    hold |= 0X04000000; // C Stick Left
                }
                else if(gp.axes[2] > STICK_PRESSED) {
                    hold |= 0X02000000; // C Stick Right
                }
                if(gp.axes[3] < -STICK_PRESSED) {
                    hold |= 0X01000000; // C Stick Up
                }
                else if(gp.axes[3] > STICK_PRESSED) {
                    hold |= 0X00800000; // C Stick Down
                }
                gamepadState.hold = hold;
                gamepadState.tpTouch = tpTouch;
                gamepadState.tpX = tpX * 854 / 320;
                gamepadState.tpY = tpY * 480 / 212;

                gamepadState.lStickX = gp.axes[0]; // Circle Pad X
                gamepadState.lStickY = gp.axes[1]; // Circle Pad Y
                gamepadState.rStickX = gp.axes[2]; // C Stick X
                gamepadState.rStickY = gp.axes[3]; // C Stick Y

                MySocket.send("gamepadstate=" + JSON.stringify(gamepadState));
            }
        </script>
    </head>
    <body>
        <div id="out"></div>
        <canvas id="canvas" width="320" height="212"></canvas>
    </body>
</html>

By the way, I was thinking of giving you credits in UsendMii for helping me with the New Nintendo 3DS. Do you prefer if I put jsa or your real name?
 

jsa

Well-Known Member
OP
Member
Joined
Oct 21, 2015
Messages
224
Trophies
0
Location
Devon, UK
Website
muffinti.me
XP
396
Country
United Kingdom
Actually I have the exact opposite :)

Here is my code for the untested beta 4 with touch support:
HTML:
<!doctype html>
<html>
    <head>
        <meta http-equiv="content-type" content="text/html; charset=utf-8" />
        <meta name="viewport" content="width=400 user-scalable=no" />
        <link rel="shortcut icon" href="favicon.ico" type="image/vnd.microsoft.icon" />
        <link rel="stylesheet" type="text/css" href="bootstrap/css/bootstrap.min.css" />
        <link rel="stylesheet" type="text/css" href="bootstrap/css/bootstrap-theme.min.css">
        <link rel="stylesheet" type="text/css" href="usendmii.css" />
        <title>UsendMii</title>
        <script type="text/javascript">
            const STICK_PRESSED = 0.06;
            var MySocket = new WebSocket("ws://" + window.location.host);
            var interval;
            var tpTouch = 0, tpX = 0, tpY = 0;

            MySocket.onopen = function()
            {
                document.getElementById("out").innerHTML = "Connected";

                var el = document.getElementsByTagName("canvas")[0];
                el.addEventListener("touchmove", handleMove, false);
                el.addEventListener("touchstart", handleStart, false);
                el.addEventListener("touchend", handleEnd, false);

                setInterval('update()', 10);
            };

            MySocket.onmessage = function(evt)
            {
                document.getElementById("out").innerHTML = evt.data;
            };

            MySocket.onclose = function()
            {
                document.getElementById("out").innerHTML = "Disconnected";
                clearInterval(interval);
            };

            function handleMove(evt) {
                evt.preventDefault();
                var touch = evt.changedTouches[0];
                tpX = touch.pageX;
                tpY = touch.pageY;
            }

            function handleStart(evt) {
                evt.preventDefault();
                tpTouch = 1;
            }

            function handleEnd(evt) {
                evt.preventDefault();
                tpTouch = 0;
            }

            function update()
            {
                if(MySocket.bufferedAmount > 0) {
                    return; // There is data in the queue
                }

                var gp = navigator.webkitGetGamepads()[0];
                var gamepadState = {tpTouch:0, tpX:0, tpY:0, hold:0, lStickX:0, lStickY:0, rStickX:0, rStickY:0};
                var hold = 0;

                if(gp.buttons[0] == 1) {
                    hold |= 0X00004000; // B
                }
                if(gp.buttons[1] == 1) {
                    hold |= 0X00008000; // A
                }
                if(gp.buttons[2] == 1) {
                    hold |= 0X00001000; // Y
                }
                if(gp.buttons[3] == 1) {
                    hold |= 0X00002000; // X
                }
                if(gp.buttons[4] == 1) {
                    hold |= 0X00000020; // L
                }
                if(gp.buttons[5] == 1) {
                    hold |= 0X00000010; // R
                }
                if(gp.buttons[6] == 1) {
                    hold |= 0X00000080; // ZL
                }
                if(gp.buttons[7] == 1) {
                    hold |= 0X00000040; // ZR
                }
                if(gp.buttons[8] == 1) {
                    hold |= 0X00000008; // Start
                }
                if(gp.buttons[9] == 1) {
                    hold |= 0X00000004; // Select
                }
                if(gp.buttons[10] == 1) {
                    hold |= 0X00040000; // Circle Pad Click
                }
                if(gp.buttons[11] == 1) {
                    hold |= 0X00020000; // C Stick Click
                }
                if(gp.buttons[12] == 1) {
                    hold |= 0X00000200; // Up
                }
                if(gp.buttons[13] == 1) {
                    hold |= 0X00000100; // Down
                }
                if(gp.buttons[14] == 1) {
                    hold |= 0X00000800; // Left
                }
                if(gp.buttons[15] == 1) {
                    hold |= 0X00000400; // Right
                }
                if(gp.axes[0] < -STICK_PRESSED) {
                    hold |= 0X40000000; // Circle Pad Left
                }
                else if(gp.axes[0] > STICK_PRESSED) {
                    hold |= 0X20000000; // Circle Pad Right
                }
                if(gp.axes[1] < -STICK_PRESSED) {
                    hold |= 0X10000000; // Circle Pad Up
                }
                else if(gp.axes[1] > STICK_PRESSED) {
                    hold |= 0X08000000; // Circle Pad Down
                }
                if(gp.axes[2] < -STICK_PRESSED) {
                    hold |= 0X04000000; // C Stick Left
                }
                else if(gp.axes[2] > STICK_PRESSED) {
                    hold |= 0X02000000; // C Stick Right
                }
                if(gp.axes[3] < -STICK_PRESSED) {
                    hold |= 0X01000000; // C Stick Up
                }
                else if(gp.axes[3] > STICK_PRESSED) {
                    hold |= 0X00800000; // C Stick Down
                }
                gamepadState.hold = hold;
                gamepadState.tpTouch = tpTouch;
                gamepadState.tpX = tpX * 854 / 320;
                gamepadState.tpY = tpY * 480 / 212;

                gamepadState.lStickX = gp.axes[0]; // Circle Pad X
                gamepadState.lStickY = gp.axes[1]; // Circle Pad Y
                gamepadState.rStickX = gp.axes[2]; // C Stick X
                gamepadState.rStickY = gp.axes[3]; // C Stick Y

                MySocket.send("gamepadstate=" + JSON.stringify(gamepadState));
            }
        </script>
    </head>
    <body>
        <div id="out"></div>
        <canvas id="canvas" width="320" height="212"></canvas>
    </body>
</html>

By the way, I was thinking of giving you credits in UsendMii for helping me with the New Nintendo 3DS. Do you prefer if I put jsa or your real name?
Either just jsa or both.
 

Crayon2000

Well-Known Member
Member
Joined
Feb 4, 2013
Messages
131
Trophies
1
XP
738
Country
Canada
@jsa someone reported a problem with my touch API implementation on the New Nintendo 3DS with my application. The major problem is scrolling. Did you do any tricks on your Touch Events Demo to prevent that? I know that on 3DBrew they recommend calling scrollTo frequently. What do you think?
 

Site & Scene News

Popular threads in this forum

General chit-chat
Help Users
  • BakerMan
    The snack that smiles back, Ballsack!
    SylverReZ @ SylverReZ: https://www.msn.com/en-gb/news/offbeat/twitch-streamer-places-24000-hit-on-youtuber-after-stellar...