Show HN:90s.dev——一款运行在网页上的游戏制作器
Show HN: 90s.dev – Game maker that runs on the web

原始链接: https://90s.dev/blog/finally-releasing-90s-dev.html

我创建了90s.dev/os/,这是一个基于Web的游戏制作环境,灵感来自Pico-8和Tic-80等复古平台,但具有现代功能。它使用320x180的画布,并在Web Worker中运行应用程序以提高安全性和性能,并提供对WebGL2的访问,从而实现60fps的游戏。它具有一个TypeScript优先的SDK,允许导入WASM模块,并包含基本的像素艺术创作工具。一个关键创新是具有自动布局系统、“refs”(可观察指针)和“composites”的GUI API,从而实现抽象视图的实现。应用程序和库可以通过GitHub或NPM使用特殊的导入路径直接共享。目标是培养一个社区,用户可以在其中创建和共享应用程序、游戏资产和游戏,所有这些都通过GitHub托管的问题跟踪器、论坛和维基进行协调。

近期上线的网页游戏制作工具90s.dev 收到了大量反馈。作者90s_dev 认为发布过早,但许多评论者鼓励他快速迭代,频繁发布。该项目旨在提供一个易用的游戏原型制作环境,其灵感来自Pico8,并结合了VS Code和TypeScript的支持。 主要功能包括一个320x180的网页画布和一个用于创建游戏和游戏制作工具的API。用户可以开发应用程序、库和游戏资源,并 potentially 与他人共享。内置应用程序,如字体制作器和一个尚未完善的绘图应用程序,都是使用相同的API构建的。 一些用户,尤其是在Firefox及其衍生浏览器上,遇到了问题,90s_dev正在积极解决这些问题。虽然该平台目前依赖于GitHub和npm,但正在计划支持直接从HTTPS URL导入,以提高灵活性。作者承认解释该项目具有挑战性,并计划在未来的版本中通过更多教程和示例来改进用户入门体验。

原文

I’ve mentioned this a bunch on HN and I’ve been working on it nonstop since about February. So I’m pretty excited to finally make it public.

The dream

Ever since I was a kid, I wanted to recreate Warcraft II or at least Warcraft I. But for decades, I never got around to it. One day this February, I just got up at 2am and started writing code. I was tired of waiting. So I wrote and wrote and wrote.

But instead of making a game, or even a game engine, or even the game maker tools, I found myself making an API for making game maker tools and a game engine and a game. It turns out, I’m really an API designer at heart. I guess I always kinda knew this.

Eventually it evolved into an API around a 320x180 canvas for building and sharing apps, whether a game maker tool like map-maker, or a 60 fps game.

New game maker that runs on the web

In short, 90s.dev/os/ is a unique new kind of game maker:

  • Runs in the browser using an HTML canvas for portability.
  • Has a 320x180 (16:9) screen that scales up to fill the window.
  • Runs all apps inside web workers for security and performance.
  • Gives you full access to WebGL2 via OffscreenCanvas for 60 fps games.
  • Lets you publish and load apps located on GitHub or NPM.
  • Has a new GUI API that has legitimate innovations, even in 2025.
  • Comes with a TypeScript-first VSCode-ready SDK for fast prototyping.
  • Allows importing modules written in any languages that compile to wasm.

By default, it will come with basic apps for making pixel art data for games, such as paint, sprite-maker, and map-maker. Someone else will have to make the sound-effect-editor and music-editor, since I know nothing about web audio APIs.

But that’s the beauty and point of it, you can make those apps and publish them, and they will be first-class apps that anyone can run, and that you can share with an iframe or link.

For example, click this to open my 40%-finished paint app, and click again to close it: /os/#sys/apps/paint.app.js

Inspired by gamedev prototyping apps

It’s inspired by pico8, tic80, picotron, and love2d:

  • Like pico8, it aims for aesthetic minimalism and supports just one language.
  • Like tic80, it lifts most of the restrictions of pico8.
  • Like love2d, it requires an external IDE for actually writing code.
  • Like picotron, it uses an operating system architecture to run apps.

You could think of it as a meta-pico8, or a love2d with TypeScript, or a tic80 that added extensibility vertically instead of horizontally.

Genuine GUI innovations

I honestly wasn’t trying to innovate here. All I wanted was a simple GUI that let me build the side panel in Warcraft I and II with its action buttons like Move and Attack.

So I wrote a very typical view API, where views draw themselves to screen and have child views. For a while, it was very ordinary and boring, which for a GUI is a good thing.

Layout

But I got tired of manually positioning and resizing all the views in a tree, so I ended up with an extremely simple auto-layout system that also happens to be robust.

Refs

I also got tired of manually setting values such as size, children, or background color, especially when most of the time it was in response to another value changing, and the new value was directly based on that changed value. I also got tired of adding callbacks to everything.

So refs gradually emerged, basically watchable pointers, and after about a month or so, they became so stable that I reworked the view APIs so that all view properties are backed by refs. This means all properties are watchable, and you can give a ref to any property.

Note: Even though refs share the same name as similar concepts in other systems, I designed mine from the ground up, with zero inspiration from those other systems. They’re nothing like React refs, and I didn’t even know Vue had refs until last week.

Composites

And finally, I stumbled on an interesting property of JSX. It turns out that if you reverse the way HTML and React use strings vs values for JSX tags, string tags become the perfect way to decouple an abstract view’s implementation from its usage. With a concrete (capitalized) view, you have to import it and use it directly. But an abstract (lowercase) view can be added to a global table by some library or app, and then used by an entirely separate part of the system.

This lead to composites:

import { Button } from '/os/api.js'

const b1 = <Button onClick={...}>...</Button> // concrete
const b2 = <button onClick={...}>...</button> // abstract

This is a surprisingly powerful concept for GUI app development. The concrete view above is always going to look and work the same. But the abstract view above can be implemented in any way at all.

This is most clear with the colorpicker view:

const $color = $(0x00000000)
const view = <colorpicker $color={$color}/>

The default implementation of this has a bare-basic color picker, that allows only 48 colors from two 24 color palettes (sweet24 and vinik24).

But any app or library developer can override this to return any kind of color picker they want. Maybe a more traditional one that has sliders for RGBA and has 16 million colors, or maybe one that lets you choose from a large list of palettes, or maybe one that mimicks pico8’s exactly.

A note on publishing apps

Until just a couple days ago, there was a shared drive in the built-in file system called net/ that was backed by a database, and the way to share apps or libraries or game assets was to copy your files into your own folder in this drive. You could easily import modules via:

import { stuff } from '/os/fs/net/someuser/some/file.js'

And it just worked, thanks to a service worker.

But this weekend, I had an epiphany of how to use the same service worker to import files hosted on NPM or GitHub, via CDN. So I removed net/ and decided to support this:

import { stuff } from '/os/fs/ghb/someuser/[email protected]/some/file.js' // or:
import { stuff } from '/os/fs/npm/someuser/[email protected]/some/file.js'

This is in the works, but it’s mostly done.

Since this is designed as an operating system, where the screen is just a 320x180 canvas, it needs apps. The built-in apps will be serviceable at best.

Ideally, the community makes their own apps, and uses those apps to make game assets and games, and all of these get shared with other users.

Since this requires coordination among the community, there is an issue tracker, wiki, and discussion forum in the sidebar, each hosted in a GitHub repo.

  • Issues: For feature requests and bug reports

  • Forum: The announce and discuss projects

  • Wiki: To collectively curate and find projects

For sharing apps, use the link format /os/#app, e.g.

联系我们 contact @ memedata.com