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.
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):
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 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
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;
}
}
}
})
}
I tried Retroassembly, not work too.
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
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).
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)
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.