105 comments

[ 3.3 ms ] story [ 168 ms ] thread
Most of this code dates back to 1998, when I built a little 2 player Joust game to push the bounds of what you could do with Div (and at the time Layer) tags in the latest browsers such as IE4 and Netscape 3. Surprisingly, most of it still worked when I thawed it out recently, provided your browser knew about document.all.

Over the weekend, I brought it up to date a bit and introduced it to Socket.io. Thus far it hasn't seen more than a dozen players at a time, so I'm curious to see what happens when a bunch of folks jump in at once.

I'll stick around to answer questions (and likely post-mortem the bits that catch on fire).

It's probably obvious for people who already played the game, but a quick note mentioning 'the highest one win' would make things easier for newcomers.

I was a bit confused about how to fight, before checking the wikipedia page

I just joined for a quick session - just wanted to say that this is really fun and very well done (especially for someone who has played Joust before). Thanks for sharing!
Fantastic :)

It would be nice if you could change the scaling to use nearest neighbour on the players so they don't look all smooged,

The modern way to implement this is with <canvas>, which lets you do that sort of thing pretty easily-- and is more performant to render.
I don't know what the obsession is with blocky graphics; they didn't look blocky back in the day, so maybe faux-nostalgia?
It's just vanilla javascript, html and css. Pull up dev tools and notice this in the css:

  img {
    image-rendering: pixelated;
  }
Lop that off (or uncheck it in dev tools) and you can have things as blurry as you please.
I played Joust a lot as a kid, the character movement looks a really good reproduction of the original to me.
If you can figure out a way to only send inputs for each player instead of their full state, it should significantly reduce network traffic and lag for each player-- right now it's sending ~1KB/frame!
Operational transformations (OTs).
I don't see "game" mentioned in the wiki at all. Is that something typically used for games or do they tend to use something else?
Collaborative text editing is what it’s traditionally used for.
It can also be used to mutate hard game state (I wouldn't try it with dynamic, interpolated values like object position, angle, acceleration, or velocity.)

There are many other methods, some are more precise and compressible than others:

Trailing State Synchronization

https://www.cs.ubc.ca/~krasic/cpsc538a/summaries/38/

Basic ideas

https://www.gafferongames.com/post/state_synchronization/

https://engineering.monstar-lab.com/2021/02/09/Game-server-S...

https://www.gabrielgambetta.com/client-side-prediction-serve...

You'd have to resync fairly often to avoid desync especially for Joust where positioning during fighting is pretty important.
Desync is possible with UDP protocols, where a dropped packet would mean lost inputs, but this uses websockets, which are reliable TCP connections, so just the list of inputs combined with a deterministic engine is sufficient.
They can arrive out of order so you still have to deal with rewinding an re'simulating' the whole sequence of moves since the skipped packet then dealing with resolving the client vs server when they disagree.
The TCP connection is never closed with websockets.
That doesn't change that TCP packets can arrive out of order does it? (It's been a while since my college networking class and it's not something I come into contact with in my job)
Yeah, 700b for 20 players, and normally at least somebody does something every tick, so that will go out 30 times per second. It still only works out to 24kbps, so you might not need to upgrade the modem you had back when the original was in the arcades.

I tried only sending updates every 3rd tick, but even there I could notice the difference.

You're right that it'd be worth experimenting with sending keystrokes instead, and backing off the full resync to once/second or so.

That's pretty awesome.

I remember making a CGI (that's an ancient, simple server backend for the young-ins) IM chat in 1996. It used the keep connection open trick present in HTTP 1.0 and a tiny bit of Netscape JS.

This is superb, addicting as hell, and now I have to force myself to stop because I have real work to do.

The game gets increasingly jittery as it gets busier. It might be worth writing some tests to simulate lots of players moving around so it's easier to tune the performance.

Glancing at the code, it looks like he's rendering everything via DOM elements. I think player sprites are positioned using the top/left properties; could potentially optimize by keeping those props constant and just updating `transform: translate(x,y)` instead...
That's a holdout from 1998. Can't go changing things up after all that time, can we?

I imagine today I would have built the whole thing in canvas. But it's cool how quick you can get something up and running if you let the browser do most of the work.

(comment deleted)
Played 5mins, had a blast. Great job :)
I have no idea what's going on. I can't tell if that's intentional or if it's broken.
80's were a crazy time
I blame pastels, bad fashion, Saved by the Bell, and Trapper Keepers.
Are those Bugle Boy jeans you're wearing?
It's both, and it's glorious.
(comment deleted)
Awesome. I loved Joust as a kid and spent countless hours playing it. Unfortunately the physics don't work as well as in the original. When you hit another player, the bounce is missing (and the egg, but I guess that's not important).
Joust, Blaster Master, Jackal, Life Force, Raptor: Call of the Shadows, and Arkanoid II. :)

Found Tyrian a few years back.

Thats awesome. Would love to see match statistics
You can see current leaderboard if you press Tab
It's a little confusing when the name doesn't get dimmed or disappears when the player gets knocked off the bird. It's almost as if it's the name of the birds only, and the players are entirely nameless ;-)
I mean, how often do you hear the name of the jockey in horse racing?
For the hardcore players you can switch teams while playing for optimal tactics.

You can also iterate between Space + Up + W keys to boost your jumps.

On Safari using an iPhone XR, the left and right movement buttons are obscured by the bottom navigation bar.
Just use Chrome. It is the sane choice.
There is no choice on iPhone
Christ what a platform. I see, and stand corrected.
Came to say the same about the original iPhone SE. I thought it was because mine is a smaller model (most sites look weird on it these days). But now I guess that's not the reason.
Yeah, mobile safari is annoying in that it doesn't allow fullscreen mode for anything except video. And that it doesn't always leave you in a consistent scroll state or with knowledge of how much screen is visible. So the left/right buttons drop off the screen unless you scroll it a bit before hitting start.
This is magnificent.
Why not implement some kind of chat feature as well? Maybe push enter to type something and it will appear above your head.
I have a joust-esque game as well but never released it because of fear with copyrighted graphics. Is that a risk with something like this?
Tried to toggle sound, now it's down.

> We encountered an error when trying to load your application and your page could not be served. Check the logs for your application in the App Platform dashboard.

All I get, no matter how many times I reload is,

Error

--

We encountered an error when trying to load your application and your page could not be served. Check the logs for your application in the App Platform dashboard.

Yeah, shame I wasn't around when this got popular (I actually submitted it on Tuesday, but HN must have automatically resurrected it for a second try).

The whole thing is running on a $5 Digital Ocean Apps server (well, two actually, one in New York, one in Frankfurt so that I can get low latency here in France), so it's amazing that it survived as long as it did.

Had I known it was going to get this much love, I would have spun up a dozen more servers.

HN gentle hug of death.
On a tech site like this can we please bring 'slashdotted' as the verb to describe a non-malicious DDoS?

/. atleast earned the right to coin the term.

/.ed implies /.-originated. Hug of death is the HN equivalent. /.ed / HoD seem to also be used for popularity DDoSes generally.
I love it, thanks for sharing!
Holy volume warnings. My dog left the room.
Yeah my ears hurt. This should go in that thread of "injuries due to code."
I listen to my music with my speakers set to 40%.

I had to change them to 6% to comfortably play this game.

Wish I had read this comment before starting the game. I had my phone's volume maxed out.
You know I've been playing this for a week now, and until reading your comment it had never occurred to me to put a volumeNode in the middle of the webaudio path.

I just thought I had my speakers too loud :)

Browser vim plugin prevents me from playing.
Click on the extension, then "save changes". It'll add an exception for the site.
That was awesome and brought me back to being a teenager in the 80s. Someone go build the same for Berzerk!
I had a lot of fun, but there's a big incentive to hit idle players. I would suggest making players intangible until receiving their first input on respawn.
One of my favorite arcade games ever. It's unfortunate that it doesn't have the same cultural cachet as the likes of Pac-Man and its cohorts.

I'll have to link this to my Dad so that we can have a quick deathmatch!