Giter Site home page Giter Site logo

aduros / wasm4 Goto Github PK

View Code? Open in Web Editor NEW
1.1K 16.0 154.0 4.94 MB

Build retro games using WebAssembly for a fantasy console.

Home Page: https://wasm4.org

License: ISC License

JavaScript 13.62% CSS 0.97% Shell 0.08% TypeScript 49.34% Makefile 1.31% C 18.99% Go 1.42% Rust 1.90% CMake 0.83% D 1.19% HTML 0.76% Zig 1.64% Odin 1.59% Nim 1.03% Lua 2.16% SCSS 1.51% WebAssembly 1.28% Python 0.39%
webassembly fantasy-console fantasy-computer retrogaming 2d-game-engine gamedev game-development wasm assemblyscript typescript

wasm4's Introduction


WASM-4 Logo
WASM-4

Build retro games using WebAssembly for a fantasy console

About

WASM-4 is a low-level fantasy game console for building small games with WebAssembly. Game cartridges (ROMs) are small, self-contained .wasm files that can be built with any programming language that compiles to WebAssembly.

Key Features

  • No Glue Code: If you've ever tried to write even a simple "Hello World" with WebAssembly before, you'll know it usually involves writing a bunch of JS and HTML glue. WASM-4 removes all of that, games interface directly with the system through a small API.

  • Minimalist: Fantasy consoles force developers to work with limited resources. This makes them simple to learn, and easier to focus on finishing your game.

  • Language Agnostic: Use any programming language, as long as it can compile to WebAssembly. Out of the box we currently support: AssemblyScript, C/C++, D, Go, Nelua, Nim, Odin, Rust, WAT, and Zig.

  • Portable: WASM-4 is designed to run on any device that can execute WebAssembly, even outside of the web! It includes a lightweight runtime written in C that runs even low-powered microcontrollers and obsolete hardware.

  • Netplay: Instant online multiplayer, featuring rollback netcode. All games that support local multiplayer automatically support netplay. WASM-4 handles syncing controller inputs over the Internet.

🚀 60 Second Quickstart

# Install the w4 command
npm install -g wasm4

# Create a project
w4 new --assemblyscript hello-world

# Setup toolchain
cd hello-world
npm install

# Build and run your game!
npm run build
w4 run build/cart.wasm

For more info and guides, check the full documentation.

🎮 Hardware Specs

  • Display: 160x160 pixels, 4 customizable colors, updated at 60 Hz.
  • Memory: 64 KB linear RAM, memory-mapped I/O, save states.
  • Cartridge Size Limit: 64 KB.
  • Input: Keyboard, mouse, touchscreen, up to 4 gamepads.
  • Audio: 2 pulse wave channels, 1 triangle wave channel, 1 noise channel.
  • Disk Storage: 1024 bytes.

🙏 Contributing

Contributions are welcome! Here are just a few ways to help:

  • Build a game or experiment, we'll feature it on wasm4.org!
  • Improve our documentation or write a tutorial.
  • Submit a bug report or feature request on Github.
  • Answer questions on the discussions forum.
  • Implement support for a new tool or language.
  • Give the project a star on Github for visibility.

wasm4's People

Contributors

aduros avatar andre-la avatar binji avatar christopher-kleine avatar chuunimage avatar claudiomattera avatar denialadams avatar desttinghim avatar exponenta avatar fabervitale avatar ibillingsley avatar jaller94 avatar jerwuqu avatar jsyrjala avatar leighmcculloch avatar lunaamora avatar majaha avatar maxgraey avatar mrrafael-dev avatar mun-ableton avatar pfgithub avatar phcoder avatar sliv9 avatar stijnruts avatar thedavesims avatar tjpalmer avatar ttulka avatar wooster0 avatar yardanico avatar zetanumbers avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

wasm4's Issues

`w4 init` command

Similar to w4 new but instead initialize the project in the current directory. Shouldn't be hard to implement

tone parameters are in wrong order for import stubs

The Rust stub in the default uses the following declaration for tone:

pub fn tone (frequency: u32, volume: u32, duration: u32, flags: u32) {
    unsafe { extern_tone(frequency, volume, duration, flags) }
}
extern {
    #[link_name = "tone"]
    fn extern_tone (frequency: u32, volume: u32, duration: u32, flags: u32);
}

However, the APU in the javascript source code uses the following parameter order:

tone (frequency, duration, volume, flags)

The same seems to be the case for AssemblyScript, Go and C.

This doesn't cause any problems in practice, but causes editor hints to be wrong.

Possibility to set FPS

It would be handy to set FPS from the game code. This would save developers some boilerplate code.

export function update(): void {
    // next update will be called in 0.5 sec
    w4.setFPS(30);

    // ...

What do you think?

Screen not rendering properly on Pale Moon

The screen only appears on gray-and-white, and the mobile gamepad is also shown.

Screenshots

wasm4 snake on pale moon
Snake on Pale Moon 29.4.0.2
wasm4 on pale moon
Sound Demo on Pale Moon 29.1.1 and 29.4.0.2

I have also been doubting if I should even post this issue as Pale Moon isn't really a popular browser, so this will probably be marked as wontfix, but let's see...

Non-ASCII text seems to be garbled.

Creating template Rust project and modifying string literal in the first text call to contain non-ASCII UTF-8 bytes makes the game output garbled text, as if it were interpreting the bytes it as CP1252 instead of UTF-8.

This contradicts with the name of function textUtf8.

Inspecting the cartridge with wasm2wat shows that the string inside is indeed UTF-8.

Go target.json example needs global base setting

Starting playing off the go hello world, noticed my KEYS and Framebuffer were getting overwritten with variables in memory. Looks like this link from the rust linker options
"--global-base=6560"
in target.json in the go hello world cleared things up

Getting started does not work for AssemblyScript

When following the Getting started tutorial I spotted several problems:

  1. Installing w4 as global is not the best solution as it may differ for projects and it is problematic to update. Better to be installed locally and executed via npx. See https://www.assemblyscript.org/quick-start.html for inspiration.
  2. Initialization doesn't work - The result from Setup instructions is Cannot GET / for http://localhost:4444/
  3. There are library files in src - wasm4.ts is a library source, I would expect it to be referenced in package.json as a devDependencies, not in the src directory as it is not supposed to be edited by the user.

`png2src` not rendering variable types in Rust output

An issue has come up in [email protected] not present in 1.0.2. The types for non-array constants are not being rendered when running w4 png2src. I believe this only affects the Rust variant since the other supported languages don't require this type signature on constant variables.

Expected

This is the output from [email protected]

const spritesheetWidth: u32 = 16;
const spritesheetHeight: u32 = 32;
const spritesheetFlags: u32 = 1; // BLIT_2BPP
const spritesheet: [u8; 128] = [
    // ...
];

Actual

This is the output from [email protected]. I do appreciate the variable naming update to match Rust style. ❤️

const SPRITESHEET_WIDTH = 16;
const SPRITESHEET_HEIGHT = 32;
const SPRITESHEET_FLAGS = 1; // BLIT_2BPP
const SPRITESHEET: [u8; 128] = [
    // ...
];

Music sequencer

Music can be implemented right now by games calling tone() at the right times. It involves writing a bunch of code to handle the time sequencing... but it's a lot of work.

Let's build a music sequencer and a music authoring format into the runtime. The goal is a new music() method which takes something as a parameter to play music using the 4 channel audio system.

There are some approaches here for that something:

  1. A text based format similar to MML or Alda. Would be quickest to get up and running and it's really neat to program music in text. Might be too hard to use for anything meaningful though. We would also need to create an authoring environment.
  2. A binary format, exported from a graphical tool like BeepBox or FamiStudio. BeepBox looks great and is really easy to use, though it does require the right presets. We would need to write a new w4 command similar to png2src that generates the blob in source code.
  3. Use straight up MIDI?? I don't know enough about MIDI to know if this is feasible, but it seems like overkill. Tools for beginners seem lacking, but it might be ideal for pro musicians.

The music format should be usable for short sound effects too and not only background music. Maybe the function should be called something like track() or sequence() instead of music() to reflect that.

Right now I'm leaning towards BeepBox, and a w4 beepbox2src command that takes a beepbox.co URL or json export and spits out binary data in source code.

Optimize palette lookup

I see palette uses texture2D texture now. What about change it to texture1D? While wasm4 support only 4 palette slots (2-bit palette) I guess it could be even simpler approach and pass this four colours via 4 uniforms and use something like this:

// palette
uniform vec2 p0;
uniform vec2 p1;
uniform vec2 p2;
uniform vec2 p3;

uniform sampler2D framebuffer;
varying vec2 framebufferCoord;

vec4 lookup(float img, vec4 p0, vec4 p1, vec4 p2, vec4 p3) {
   // not sure about implementation. Just basic idea
   // instead "mix" it could be "step"
   vec4 p;
   p = mix(p0, p1, mix(0.0,       1.0 / 3.0, img));
   p = mix(p,  p2, mix(1.0 / 3.0, 2.0 / 3.0, img));
   p = mix(p,  p3, mix(2.0 / 3.0,       1.0, img));
   return p;
}

void main() {
  gl_FragColor = lookup(texture2D(framebuffer, framebufferCoord).r, p0, p1, p2, p3);
}

Reduce a sence for Virtual Joy

Problem:
Hard to play when Arrows Up / Left/Right is used in same time because any wrong finger movements produce false input.

I think that corner input should be registered only when a finger in those zone:
Propose

What is problem, looks video (focus on joy):
https://youtu.be/dk0RV5x2Bz4?t=16

You can figure out, that when a left/right is pressed , sometimes UP/DOWN is detected too, that produce a wrong movement.
For Snake game this extremely increasing a difficulty.

[feature] Real time

For audio sequence should be provided a timecode.
We can use a tick of update, but how i know it depend of RAF frequency, and we can't sync a APU with update for sequencing.

I know that WASM export a seed function that really is Date.now, and this already can be used, but is hacky.

maybe we can provide a time function same way?

UPD, i see that now we use a clamped update, and it should runs about 60 frames on any screens, exclude a iOS in power-save mode (it ticks 30 FPS)

https://github.com/aduros/wasm4/blob/main/runtimes/web/src/index.js#L343

Prerequisites to run rust version is not listed

It definitely require install wasm32-unknown-unknown toolchain before trying to compile any of rust code. It either require installation of wasm-pack or installing it manually via rustup. Does it installs wasm-pack when we install w4?

[bug] drawing completely outside screen overwrites border pixels

If drawing outside the screen 1 pixel to the right or 1 pixel below the SCREEN_SIZE, the border pixels within the screen boundary are overwritten. Doing so in the top or left of the screen does not exhibit the issue.

Example:

#include "wasm4.h"

void start()
{
	PALETTE[0]=0x000000;
	PALETTE[1]=0xFF0000;
	PALETTE[2]=0x00FF00;
	PALETTE[3]=0x0000FF;
}

void update()
{
	*DRAW_COLORS=0x03;
	rect(SCREEN_SIZE-16,SCREEN_SIZE-16,16,16);
	*DRAW_COLORS=0x22;
	rect(SCREEN_SIZE,SCREEN_SIZE-16,16,16);
	rect(SCREEN_SIZE-16,SCREEN_SIZE,16,16);
	
	*DRAW_COLORS=0x03;
	rect(0,0,16,16);
	*DRAW_COLORS=0x22;
	rect(-16,0,16,16);
	rect(0,-16,16,16);
}

Result:
result

Expected:
expected

Rust `text` or `trace` and `format!` throws `RuntimeError: unreachable executed`

When attempting to draw dynamic text to the screen in rust, the runtime aborts entirely.

// lib.rs
mod wasm4;
use wasm4::*;

#[no_mangle]
fn update() {
    unsafe {
        *DRAW_COLORS = 2;
    }
    text(format!("{:02x}", unsafe {CT}).as_str(), 10, 10);
    unsafe { CT += CT.wrapping_add(1) }
}

Error in the console:

Uncaught (in promise) RuntimeError: unreachable executed
    update http://localhost:4444/wasm4.js:1
    t http://localhost:4444/wasm4.js:1
    <anonymous> http://localhost:4444/wasm4.js:1
    async* http://localhost:4444/wasm4.js:1
    <anonymous> http://localhost:4444/wasm4.js:1
wasm4.js line 1 > WebAssembly.instantiate:15530:1
    <anonymous> http://localhost:4444/wasm4.js:1
    AsyncFunctionNext self-hosted:692
    (Async: async)
    <anonymous> http://localhost:4444/wasm4.js:1
    <anonymous> http://localhost:4444/wasm4.js:1

[Rust] cannot allocate a String in update

Hi,

I'm having trouble allocating a String, am I missing something?

// lib.rs
#[no_mangle]
fn update() {
    let mut my_string = String::new();

    my_string.push('#');

    text(&my_string.clone(), 20, 20);
}
Uncaught (in promise) RuntimeError: unreachable
    at update (<anonymous>:wasm-function[11]:0x20fb)
    at p.update (wasm4.js:1)
    at t (wasm4.js:1)
    at wasm4.js:1
    ```

Requesting more linear memory is broken

If you request more linear memory, then the emulator's this.memory is not actually the one in use by the WASM instance anymore and the whole thing starts crashing and / or not displaying anything anymore.

Oh unless the idea is that allocating memory isn't even allowed. Though that doesn't seem to be enforced anywhere. For example Rust by default allocates 1MiB for the stack alone on startup, almost definitely true for C and C++ as well.

[Go] Suggestion: change trace function to take array of bytes instead of string in golang

I wanted to debug some stuff and print some integers to the console. I wrote some integer printing code.

func printInt(r int) {
	var bytes [6]byte
	bytesIndex := 5
	byteLen := 0
	const MAXX = 1000_000
	for i := 10; i < MAXX; i *= 10 {
		rem := r % i
		bytes[bytesIndex] = digitToByte(rem * 10 / i)
		r -= rem
		bytesIndex--
		byteLen++
		if r < 1 || bytesIndex < 0 {
			break
		}
	}
	trace(string(bytes[:]), byteLen)
}

But after few prints the colors in game started changing. The problem were hidden memory allocations during string operations. The problem is strings in golang allocate on the heap on creation. It seems that this heap allocation doesn't care where it allocates memory and my color pallete got overwritten by string data! I think it would be better if the trace function in golang was like c++ version so it takes array of bytes so we avoid allocating memory with string type. Like this:

func trace (str []byte, byteLength int);

Platform crashes after a while (AS)

The program crashes after a while when different objects are being rendered.

To reproduce this, follow the Setup for a HelloWorld AS application and make this little change in main.ts:

// ... as in the template

let counter: i32 = 0;

export function update (): void {
    store<u16>(w4.DRAW_COLORS, 2);
    w4.text("Counter: " + (counter++).toString(), 10, 10);

// ... as in the template

image

Same behavior in Chrome and Firefox.

I spotted this when developing a game but the code above is a minimal example.

Suggestion: Make an organization

I think it's better to split the CLI, runtime, website and example into different repositories. It'll be easier to manage it.

[docs] Set compolr in docs and templetes are differ

Docs show example which uses 0x42

store<u16>(w4.DRAW_COLORS, 0x42);
w4.rect(10, 10, 32, 32);

But template uses store<u16>(w4.DRAW_COLORS, 2) for this.

Will be great explain what 0x42 mean and how it created. For example: 0x04 << 4 | 0x02. Even better have some util function like:

function encodeColors(xx: u32, yy: u32, zz: u32, ww: u32): u16 {
  return ((xx & 0x3) | (yy & 0x3) << 4 | (zz & 0x3) << 8 | (ww & 0x3) << 12) as u16;
}

Lua cartridge support

Would this be useful for anyone? Lua seems to be the lingua franca of fantasy consoles, so it might help drive adoption?

Implementation-wise it would be a little different since it's interpreted. We could provide a prebuilt cart.wasm written in C containing the Lua runtime. The *.lua files would then be injected into this cart.wasm somehow to produce the final cart.

The nice thing about this approach is that Lua carts would require no compiler toolchain to setup.

API for global leaderboard

It would be great to have the ability to save scores in the global leaderboard, which would be available for all cartridges. Such a table could then be accessed either from the game itself or/and as a link on the cartridge web page. You could even add GitHub account registration as an option.

wdyt?

[suggestion] Native Gamepad support

Will be good to intorudce a Gamepad support in game and menu.
This allow to use this platform like Steam Big Screen in AndroidTV, Tablet or Real Consoles with WebGL/Canvas support (like Xbox, PS5).

For runtime this will be easy, and because site have grid, looks like that Gamepad navigation can be implemented and on it.

Web-based playground editor

It would be neat to have an easily accessible editor for live coding. Something like the TypeScript Playground with the code on the left and the game on the right, with the ability to inspect the WAT and download the cart.wasm.

AssemblyScript seems like it would be a perfect fit for this! @MaxGraey 👀 Maybe the existing AS editor could be repurposed?

Improved international keyboard support in demos

The playable demos have a bad UX on non-EN keyboard layouts.
Please add a few alternative keys, e.g., for Watris:

  • Add C as more universal "hard drop" key (Z is always a bad choice as an action button, e.g., for DE and FR layouts, where it is not next to X).
  • ArrowUp as "spin" key and ArrowDown as "hard drop" key, to allow full one handed control on any keyboard (and making it playable for one-handed people).

And, I guess there are also other smart choices for the other demos, for good UX worldwide, and for one handed control.

[bug, site, runtime] Strange bugs on iPad OS (iOS) 15

I think this issue related that iPAD have a layout as in MacOS but has touchscreen.
I know that a lot of sites not preventing a zoom in/out by double tap or pinch (mapped on wheel) and this produce bugs like this.

But why a virtual joypad is missaligned and stacking?

https://youtu.be/59GZ0Chnn8Q

This reproduced in Safari and Chrome (because it use Safari).

Then a Safari not support 'pixelated' on WebGL context, i think we should use internal scale by bliting a framebuffer into canvas with 'nearest' filter mode, because anyway we should try to round pixel for avoiding canvas pixel stretching when a viewport little bit smaller or greater that 160.

Patch exist, but not working in Safari
https://bugs.webkit.org/show_bug.cgi?id=193895

Complement #85

Q: Who is the user?

With a brief look at the source code, I see that the implementation (rendering, ...) is provided by the platform. This means, it is relatively easy to add support for a new language, but it is difficult to add a new platform (the whole runtime must be re-implemented).

Is the goal of the project to be extendable for language creators, or to write Wasm games that run easily on any new/exotic platform? The current implementation tends to the former.

I wonder if this was really the motivation as I would expect the opposite: Support of just a few popular languages (AS, Rust, C/C++) with ease of integration to my platform. This could be achieved by implementing the rendering in the Wasm module and provide just the update function, output bytes (the rendered scene) and user input interface. The platform would then need just to copy the bytes to whatever screen it has and call update.

What are your thoughts? :-)

[Rust] Handling of panics by passing them to trace()

Adding this to start() will hook all panic messages (probably), unless it panics during a panic in which case it just crashes. If memory problems are present it will crash on alloc so we can't rely on .to_string().

#[no_mangle]
fn start() {
    #[cfg(debug_assertions)]
    panic::set_hook(Box::new(|info| {
        if let Some(s) = info.payload().downcast_ref::<&str>() {
            trace(s);
        } else {
            trace("A panic occurred somewhere...")
        }

        /* // I am unsure of what wizardry wasmbindgen uses but backtraces don't seem to work independently.
        trace("Stack:\n");
        let backtrace = Backtrace::force_capture();
        match backtrace.status() {
            std::backtrace::BacktraceStatus::Unsupported => trace("No backtrace support"),
            std::backtrace::BacktraceStatus::Disabled => {
                trace("Something disabled the backtracing")
            }
            std::backtrace::BacktraceStatus::Captured => {
                for frame in backtrace.frames() {
                    trace(&format!("{:?}", frame));
                }
            }
            _ => trace("The compiler demanded I add this. If you see this assume everything has gone wrong."),
        };
        */

        //trace(&info.to_string())
        // .to_string() will just crash if allocations are not working
    }));
}

An error(&str) function could be added so that stack logs could be passed to the console as errors.

[Bug] Oval

I begun implement new feature but see that we have a lot of bug in oval

wasm4-screenshot

import * as w4 from "./wasm4";

let time:  f32 = 0.0;

export function update (): void {
    time += 0.01;

    store<u16>(w4.DRAW_COLORS, 2);

    const sx = u32((1. + Mathf.sin(time)) * 50);
    const sy = u32((1. + Mathf.sin(time)) * 50);

    w4.text ("New Rect", 4, 4);
    w4.rect (20, 20, 31, 31);

    w4.text ("New Oval", 84, 4);
    w4.oval (80 + 20, 20, 31, 31);

}

Follow: #13

Palette does not work as expected

Consider this AS code:

import * as w4 from "./wasm4";

store<u32>(w4.PALETTE, 0xe0f8cf, 0);    // light
store<u32>(w4.PALETTE, 0xe30000, 1);    // red
store<u32>(w4.PALETTE, 0x306850, 2);    // green
store<u32>(w4.PALETTE, 0x000000, 3);    // black

export function update (): void {
  // nothing here
}

I would expect the background colored in light green, but some kind of violet appears:

image

Works fine when the other three colors are not set.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.