I'm @arianrhodsandlot, a frontend engineer committed to delivering clean, efficient, maintainable, high-quality code and user-friendly products.
You can find some of them that are open-source below, and feel free to raise any feedback.
A JavaScript library used for running emulators of retro consoles inside browsers.
Home Page: https://nostalgist.js.org
License: MIT License
I'm @arianrhodsandlot, a frontend engineer committed to delivering clean, efficient, maintainable, high-quality code and user-friendly products.
You can find some of them that are open-source below, and feel free to raise any feedback.
Hi, I've been trying to solve this for days and I can't understand it, using a custom core yesterday I just compiled it and I get the following error
515e234c-212a-49a4-ae62-6c4ca14139b6:7 Uncaught ReferenceError: AL is not defined
at getEmscripten (515e234c-212a-49a4-ae62-6c4ca14139b6:7:18)
at Emulator.setupEmscripten (nostalgist.js:17498:30)
at async Emulator.launch (nostalgist.js:17714:9)
at async t.launchEmulator (nostalgist.js:18458:5)
at async t.launch (nostalgist.js:18257:23)
at async t.launch (nostalgist.js:18083:5)
at async :8:17
the error occurs on the following loading line
var shouldRunNow = true;
if (Module["noInitialRun"])
shouldRunNow = false;
run();
;Module.FS = FS;
Module.PATH = PATH;
Module.ERRNO_CODES = ERRNO_CODES;
return {
**AL,**
Browser,
JSEvents,
Module,
exit: _emscripten_force_exit
}
I tried compiling my own systems using the retroarch guide and I also tried using colors that I got from https://web.libretro.com/, they all generate the same error and I don't know why
Hi. There are few more cores that work great with Nostalgist (in addition to the ones you have uploaded to https://github.com/arianrhodsandlot/retroarch-emscripten-build/tree/main/retroarch ):
Atari 400 / 800 / XE / XL series
https://github.com/libretro/libretro-atari800
(If you try to compile this one and encounter any errors let me know, I may have some uncommitted changes)
Atari 2600 / VCS
https://github.com/libretro/stella2014-libretro
Amstrad CPC series
https://github.com/libretro/libretro-crocods
To build the above - I cloned the projects and cd its directory, then
emmake make -f Makefile platform=emscripten
then changed the output filename to libretro_emscripten.bc and copied it to RetroArch directory, then (from this dir)
emmake make -f Makefile.emscripten LIBRETRO=corename -j all
(where corename is e.g. stella2014, atari800, crocods)
Apart from that, the EmulatorJS has a working Commodore Amiga core (that I cannot build at the moment due to emsdk issue?):
https://github.com/libretro/libretro-uae
Hi there, I'm the maintainer of BrowserFS. I've been going though the dependents of browserfs.
Please see the notice in the readme: https://github.com/jvilk/browserfs
Would you be interested in updating your project to use the updated fork, ZenFS?
Related to #4, Some useful tools for multiplayer and recording/playback would be related to knowing the frame.
{ startPaused: true }
starting paused would allow a group of players to prepare the games and start at relatively the same time.getCurrentFrame()
getting the current frame would be useful for knowing when a player did an action. Good for broadcasting actions to happen in the future or for recording the action to be played lateron("frame", ()=>{})
knowing what frame the player is on would be good commanding prepared actions. For my examples, the event happens before the buttons are read.This might be more work than it's worth but it seems interesting
// Not working Example Multiplayer Client
const controller = new OnscreenController(document.querySelector("#OnscreenController"))
const connection = new WebSocket("/game-room/12345");
const nostalgist = await Nostalgist.launch({
startPaused: true, // Needed so that all players start around the same time
core: 'fceumm',
rom: 'flappybird.nes',
})
await syncWithRoom(connection);
const frameOffset = await getFrameOffset(connection)
var startListener = (event)=>{
const data = JSON.parse(event.data)
if(data.action !== "start") return;
connection.removeEventListener("message", startListener)
nostalgist.resume();
}
connection.addEventListener("message", startListener)
const ACTIONS_TODO = {};
controller.addEventListener("button", (event)=>{
const { button, toggleValue } = event.data
const targetFrame = nostalgist.getFrame() + frameOffset;
connection.send({
targetFrame, button, toggleValue
})
})
// Doesn't have to be event target. just simpler to use it for the example
nostalgist.addEventListener("frame", (event)=>{
const currentFrame = event.frame;
const todos = ACTIONS_TODO[currentFrame];
if(todos === void 0) return;
delete ACTIONS_TODO[currentFrame]
todos.forEach((todo)=>{
nostalgist.press(todo.player, todo.button, todo.toggleValue)
});
})
connection.addEventListener("message", (event)=>{
// Handle Input
const data = JSON.parse(event.data);
if(data.action !== "input") return;
const { targetFrame, player, button, toggleValue } = data
const currentFrame = nostalgist.getFrame()
if(targetFrame <= currentFrame){
nostalgist.exit();
connection.send(JSON.stringify({
action: "desync"
}));
alert("You are out of sync with the room")
return;
}
if(!(targetFrame in ACTIONS_TODO)){
ACTIONS_TODO[targetFrame] = []
}
ACTIONS_TODO[targetFrame].push({ player, button, toggleValue })
})
connection.addEventListener("message", (event)=>{
const data = JSON.parse(event.data);
if(data.action !== "desync") return;
nostalgist.removeController(data.player)
})
// Not working Example Input Recording
const PLAYER = 0;
// Recording
function runRecorder(){
const controller = new OnscreenController(document.querySelector("#OnscreenController"))
const nostalgist = await Nostalgist.launch({
startPaused: true, // Needed so that all players start around the same time
core: 'fceumm',
rom: 'flappybird.nes',
})
const transcript = [];
controller.addEventListener("button", (event)=>{
const { button, toggleValue } = event.data
transcript.push({ frame: nostalgist.getFrame(), type: "button", button, toggleValue });
nostalgist.press(PLAYER, button, toggleValue)
})
return new Promise((res)=>{
nostalgist.addEventListener("exit", ()=>{
transcript.push({ frame: nostalgist.getFrame(), type: "off" })
res(transcript)
})
});
}
// Playback
function playTranscript(transcript){
var transcriptOffset = 0;
nostalgist.addEventListener("frame", (event)=>{
const currentFrame = event.frame;
for(transcriptOffset; transcriptOffset < transcript.length; transcriptOffset++){
const action = transcript[transcriptOffset]
if(action.frame > currentFrame) break;
switch(action.type){
case "button": {
nostalgist.press(PLAYER, action.button, action.toggleValue);
break;
}
case "exit":{
nostalgist.pause();
alert("recording finished");
break;
}
}
}
})
}
Hello,
Thank you for creating this awsome project but I have question.
When loading some NES game sometimes it load slow it just show black canvas I want to add loading screen how can I do that?
Thanks
This is a repo reproducing this question: https://github.com/daGaiGuanYu/show-me-bugs/tree/nostalgist240207
warn message in console:
The AudioContext was not allowed to start. It must be resumed (or created) after a user gesture on the page.
I don't know whether this can be fixed.
If not, it would be nice to mention it in the documentation.
Developer could show a dialog to tell users "rom download, start now?".
And user's clicking will offer a "user gesture", and this limit are gone.
I'm making a little APP (not completed):
I'm trying to make systems that use bios like pcsx and opera work, both always give the following errors:
1 RuntimeError: unreachable
at 00c64f1a:0xd1d6a
at 00c64f1a:0x13665
at 00c64f1a:0x1b5e4e
at ret. (8fbee732-1b0d-4fb8-9da6-d319b3e9890e:10:198235)
at invoke_iii (8fbee732-1b0d-4fb8-9da6-d319b3e9890e:10:218105)
at 00c64f1a:0xb88a7
at 00c64f1a:0x9fc18
at 00c64f1a:0x18ceac
at ret. (8fbee732-1b0d-4fb8-9da6-d319b3e9890e:10:198235)
at Module._main (8fbee732-1b0d-4fb8-9da6-d319b3e9890e:10:214762)
465nostalgist.js:17219 1 RuntimeError: null function or function signature mismatch
at 00c64f1a:0x1ed77d
at 00c64f1a:0x1aa199
at 00c64f1a:0x1b6518
at ret. (8fbee732-1b0d-4fb8-9da6-d319b3e9890e:10:198235)
at browserIterationFunc (8fbee732-1b0d-4fb8-9da6-d319b3e9890e:10:185714)
at callUserCallback (8fbee732-1b0d-4fb8-9da6-d319b3e9890e:10:85871)
at Object.runIter (8fbee732-1b0d-4fb8-9da6-d319b3e9890e:10:87289)
at Browser_mainLoop_runner (8fbee732-1b0d-4fb8-9da6-d319b3e9890e:10:85436)
Is there any configuration that I'm not including?
Hi!
I think that there’s a bug that causes all rom imports from external CORS-enabled domains to fail. It's easy to reproduce this with Archive.org URLs - the Internet Archive allows cross-origin downloads (and they work fine when using EmulatorJS).
I think I can post a public domain / demoscene prod link (Game Boy) for reference :)
either
rom: 'https://archive.org/download/pouet_54175/oh.gb',
or
rom: 'https://archive.org/download/pouet_54175/oh.zip/oh.gb',
(plain http URLs can also be used on archive org)
I want host nostalgist.js on my pc so i can play games from localhost instead of the nostalgist.js website.
I also want to run this on a circuitpython device (it is an raspberry pi pico w).
Looks like a cool project. One thing I noticed in the instance methods is that there are no manual input methods. Granted, keyboard and gamepad may be enough to control/play the game but a developer might want to have "on screen" controls for things like mobile devices. In addition, a player might want to customize the inputs of a gamepad to their liking or have something like a turbo (when a player doesn't want to mash a button) or toggle (so you don't have to hold down the run button in mario)
If you want to point me in the direction of where the inputs are handled I wouldn't mind taking a look at what I can do
Hey, I could not manage to get access to rom's memory.
I think retroArch provides such feature, could we get this in Nostalgist?
Hi there, would you have an example to have a callback for every frame? I'd like to interact with the emulator at every frame rendered. Thanks!
(specifically for the NES core)
I tried Retroassembly, not work too.
Hello, I want to change some games to an online version. When synchronizing audio streams, I cannot obtain the audio stream. How can I read the audio stream data in the game?
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.