This is awesome. My only issue is that the character used for whitespace looks janky in my browser, like a bunch of non-monospaced squares. A potential remedy: because Unicode contains all 256 possible 4x2 Braille patterns, why not use ⣿ as the background and carve out the snake/food as negative space, e.g.
⣿⣿⣛⣛⣛⣛⣛⣩⣽⣿⣯⣿⣿⣿
This would ensure uniform spacing and is just as legible.
Yes, i thought of doing that. The problem is that, while it would definitely help on the early game, it would also mess things badly on the late game. As you snake grows, it'll take more and more space on the grid, and you'll start seeing more and more janky whitespace-replacement characters.
The game gets faster as you progress, so it's definitely not a good idea to make it jankyer when you're try-harding it :P
I'd love to know of a way of "fixing" this jankyness issue properly. Without admitting defeat and rendering to some other text-admitting output, like the page <title>, as this oher snake game that was recently posted on Reddit does: https://old.reddit.com/r/webdev/comments/1n9z77e/snake_in_th...
But, for now, if you're actually trying to get a high score, i think the best approach is rendering the URL on page, by clicking on the "?"
One thing to note about these two APIs is that they affect how the session history (the back/forward stack) behaves, but the global browser history (entries shown in the History tab) is separate.
Most browsers record every change in the global history regardless of whether `history.pushState` or `history.replaceState` is used. The HTML Spec[0] is explicit about session history but does not define how global history should behave.
I can understand why the spec makes no mention of this -- global history is a user-facing UI feature, similar to address bar autocomplete, and it makes sense for browsers to control this behavior. That said, I'm always annoyed when I look into my history tab after visiting a page like this (e.g. Vercel Domains[1]), and see my global history flooded with entries for each individual keystroke I've made, all in the name of "user experience".
In this particular case, it's just a fun gimmick, but for everyday websites I'd much prefer if they just debounced the updates to the URL to avoid cluttering the global history.
function drawWorld() {
var hash = '#|' + gridString() + '|[score:' + currentScore() + ']';
if (urlRevealed) {
// Use the original game representation on the on-DOM view, as there are no
// escaping issues there.
$('#url').textContent = location.href.replace(/#.*$/, '') + hash;
}
// Modern browsers escape whitespace characters on the address bar URL for
// security reasons. In case this browser does that, replace the empty Braille
// character with a non-whitespace (and hopefully non-intrusive) symbol.
if (whitespaceReplacementChar) {
hash = hash.replace(/\u2800/g, whitespaceReplacementChar);
}
history.replaceState(null, null, hash);
// Some browsers have a rate limit on history.replaceState() calls, resulting
// in the URL not updating at all for a couple of seconds. In those cases,
// location.hash is updated directly, which is unfortunate, as it causes a new
// navigation entry to be created each time, effectively hijacking the user's
// back button.
if (decodeURIComponent(location.hash) !== hash) {
console.warn(
'history.replaceState() throttling detected. Using location.hash fallback'
);
location.hash = hash;
}
}
For those that missed it initially, and didn't quite got how it works the first time, there is a small cyan question mark at the top left, which states:
> Use the arrow keys or WASD to control the snake on the URL. Click here if you can't see the page URL or if it looks messed up with some weird slashes
Additionally: you need a browser window where you're address bar is long enough to see the world ;).
I've actually thought about rendering DOOM on the favicon. I don't see why it shouldn't be possible. Maybe @Franciscouzo could tackle that challenge! https://news.ycombinator.com/item?id=45408825
Yeah, sorry for that. The game actually used to work decently well on mobile browsers.
If you're brave enough to try on a mobile device, there's a way to see the current URL without escaping on the page: clicking on that "?". The mobile controls are clunky, but you'll be rewarded with the ability to share your highscores with friends! :P
The source code is not minified or anything, ctrl+u is your friend :)
IDK about the AI claim tho. The game has been there for like 10 years, so it's probably in the training data of these things. The bots might be able to replicate it, but they surely won't be able to enjoy it! (for now at least)
Wow! It's such a surprise to see this old project of mine here on HN front page!
I must say, if you're experiencing any issues playing this, it's probably because it was designed to be played on the browsers of 10 years ago hehe. Here's how the game used to look and play in its former days of glory: https://github.com/epidemian/snake/blob/master/gameplay.gif?...
Since then, browsers have made some so-called "security" "improvements" that heavily hindered the capabilities of addressbar-based videogames. You can see traces of this on the game source code, and on the commit history.
At some point, pushing things to `history.replaceState()` got super rate-limited on Chrome, to something like tens of updates per minute IIRC, which totally wrecked the playing experience. I think i got around this by falling back to using `location.hash` directly. I think Chrome later rose this throttling to something more sensible. IDK if enough to play Crysis at 60fps on the addressbar, but enough for a snake game. And if not, sorry for messing up your Back button!
The worst of these security-excused changes was Firefox and Chrome starting to escape all whitespace characters (and others) on URLs. The game uses Braille characters to "render" its grid world, and blank Braille characters are abundant, especially on the early game. I think i made some comments on the browsers' issue trackers, and even received some sympathy from the developers (or maybe this was on the throttling of history, i don't remember). But of course, and as usual, "security" trumps over fun.
I ended up trying to counteract this URL escaping mechanisms with some horrible, really really horrible, indefensible, shameful, canvas-based font-measuring hack to replace blank Braille characters with some other character that doesn't get escaped and is more or less the same width, and as blank as possible. See https://github.com/epidemian/snake/blob/e9d5591a613afabc7e11.... If you have any idea of how to do this in a less soul-damning way, please let me know!
I think the game never worked properly on Safari. I know the browser used to hide the URL fragment, or maybe everything other than the domain name. I've no idea what it does now; does it even allow users to visit random webpages or does it mandate a separate app for everything? /s
In case my pile of hacks fails thoroughly, i resignedly added a way of showing the intended URL on the actual page content, by clicking on the "?"
Anyways, i should probably write a blog post about this little silly thing. Thanks for playing! :)
that looks a lot better. i am seeing the address bar filled with black and white pixelated block characters (U+2591 light shade). it still works though.
This is awesome. My only issue is that the character used for whitespace looks janky in my browser, like a bunch of non-monospaced squares. A potential remedy: because Unicode contains all 256 possible 4x2 Braille patterns, why not use ⣿ as the background and carve out the snake/food as negative space, e.g.
⣿⣿⣛⣛⣛⣛⣛⣩⣽⣿⣯⣿⣿⣿
This would ensure uniform spacing and is just as legible.
Yes, i thought of doing that. The problem is that, while it would definitely help on the early game, it would also mess things badly on the late game. As you snake grows, it'll take more and more space on the grid, and you'll start seeing more and more janky whitespace-replacement characters.
The game gets faster as you progress, so it's definitely not a good idea to make it jankyer when you're try-harding it :P
I'd love to know of a way of "fixing" this jankyness issue properly. Without admitting defeat and rendering to some other text-admitting output, like the page <title>, as this oher snake game that was recently posted on Reddit does: https://old.reddit.com/r/webdev/comments/1n9z77e/snake_in_th...
But, for now, if you're actually trying to get a high score, i think the best approach is rendering the URL on page, by clicking on the "?"
I made a similar thing some time ago, but with the favicon.
https://franciscouzo.github.io/favisnake/
Likewise, I did 2048 with the favicon years ago
https://aquova.net/games/2048/
Oh, this is lovely. The more retina, the less playable it gets :D
Update: amazing game-over effect!
I was pleasantly surprised by how responsive it was, and overjoyed when I clicked back and immediately came back to HN: no messy history. Genius idea!
history.pushState vs history.replaceState
One thing to note about these two APIs is that they affect how the session history (the back/forward stack) behaves, but the global browser history (entries shown in the History tab) is separate.
Most browsers record every change in the global history regardless of whether `history.pushState` or `history.replaceState` is used. The HTML Spec[0] is explicit about session history but does not define how global history should behave.
I can understand why the spec makes no mention of this -- global history is a user-facing UI feature, similar to address bar autocomplete, and it makes sense for browsers to control this behavior. That said, I'm always annoyed when I look into my history tab after visiting a page like this (e.g. Vercel Domains[1]), and see my global history flooded with entries for each individual keystroke I've made, all in the name of "user experience".
In this particular case, it's just a fun gimmick, but for everyday websites I'd much prefer if they just debounced the updates to the URL to avoid cluttering the global history.
[0]: https://html.spec.whatwg.org/#navigation-and-session-history
[1]: https://vercel.com/domains
reading the source it looks like for some browsers that rate limit url updates, it has to use a different way that nukes your back button ability.
function drawWorld() { var hash = '#|' + gridString() + '|[score:' + currentScore() + ']';
What a neat little idea lol. It's using the Braille patterns to update the URL with the dots. Love it.
https://github.com/epidemian/snake/blob/master/snake.js
This is why i come to hackernews. :) made my day
For those that missed it initially, and didn't quite got how it works the first time, there is a small cyan question mark at the top left, which states:
> Use the arrow keys or WASD to control the snake on the URL. Click here if you can't see the page URL or if it looks messed up with some weird slashes
Additionally: you need a browser window where you're address bar is long enough to see the world ;).
Love the concept, it works quite well!
This is fun!
Such a unique, innovative idea. Well played.
Harder than I expected, the extra blank dots all around the snake are kind of distracting, how does it look when they aren't on the page?
This is how it used to look in olden days, before browsers plundered our fun in the name of security: https://raw.githubusercontent.com/epidemian/snake/refs/heads...
To an average Joe, this might not look like anything but to me this is wild. How do you people even come up with these things...
next time doom please :)
I've actually thought about rendering DOOM on the favicon. I don't see why it shouldn't be possible. Maybe @Franciscouzo could tackle that challenge! https://news.ycombinator.com/item?id=45408825
This is really creative! Very well done. Surprisingly fast too.
Very clever, and playable! Thanks.
I just see the address? Safari on iOS.
See closely there should be snake in the bar as well, which responds if you use the arrows.
>demian.ferrei.ro/snake#|%E2%96%91%E2%96%91%E2%96%91%E2%96%91%E2%A0%88%E2%96%91%E2%96%91%E2%A0%A4%E2%A0%A4%E2%96%91%E2%96%91%E2%96%91%E2%96%91%E2%96%91%E2%96%91%E2%96%91%E2%96%91%E2%96%91%E2%96%91%E2%96%91|[score:0]
This is too confusing to play on firefox.
Worked just fine on version 143.
Not for me on Android (same version)
Yeah, sorry for that. The game actually used to work decently well on mobile browsers.
If you're brave enough to try on a mobile device, there's a way to see the current URL without escaping on the page: clicking on that "?". The mobile controls are clunky, but you'll be rewarded with the ability to share your highscores with friends! :P
Very cool, would love to see source code. For what its worth no AI was able to replicate this.
The source code is not minified or anything, ctrl+u is your friend :)
IDK about the AI claim tho. The game has been there for like 10 years, so it's probably in the training data of these things. The bots might be able to replicate it, but they surely won't be able to enjoy it! (for now at least)
There's a link to "code" at the bottom of the page.
Bro the game is intense!
this is the most cursed thing i've seen
This is cool
it took me a while to understand how to play it, but holy shit this is impressive! nice work
nice i enjoyed it
Wow! It's such a surprise to see this old project of mine here on HN front page!
I must say, if you're experiencing any issues playing this, it's probably because it was designed to be played on the browsers of 10 years ago hehe. Here's how the game used to look and play in its former days of glory: https://github.com/epidemian/snake/blob/master/gameplay.gif?...
Since then, browsers have made some so-called "security" "improvements" that heavily hindered the capabilities of addressbar-based videogames. You can see traces of this on the game source code, and on the commit history.
At some point, pushing things to `history.replaceState()` got super rate-limited on Chrome, to something like tens of updates per minute IIRC, which totally wrecked the playing experience. I think i got around this by falling back to using `location.hash` directly. I think Chrome later rose this throttling to something more sensible. IDK if enough to play Crysis at 60fps on the addressbar, but enough for a snake game. And if not, sorry for messing up your Back button!
The worst of these security-excused changes was Firefox and Chrome starting to escape all whitespace characters (and others) on URLs. The game uses Braille characters to "render" its grid world, and blank Braille characters are abundant, especially on the early game. I think i made some comments on the browsers' issue trackers, and even received some sympathy from the developers (or maybe this was on the throttling of history, i don't remember). But of course, and as usual, "security" trumps over fun.
I ended up trying to counteract this URL escaping mechanisms with some horrible, really really horrible, indefensible, shameful, canvas-based font-measuring hack to replace blank Braille characters with some other character that doesn't get escaped and is more or less the same width, and as blank as possible. See https://github.com/epidemian/snake/blob/e9d5591a613afabc7e11.... If you have any idea of how to do this in a less soul-damning way, please let me know!
I think the game never worked properly on Safari. I know the browser used to hide the URL fragment, or maybe everything other than the domain name. I've no idea what it does now; does it even allow users to visit random webpages or does it mandate a separate app for everything? /s
In case my pile of hacks fails thoroughly, i resignedly added a way of showing the intended URL on the actual page content, by clicking on the "?"
Anyways, i should probably write a blog post about this little silly thing. Thanks for playing! :)
https://github.com/epidemian/snake/blob/master/gameplay.gif?...
that looks a lot better. i am seeing the address bar filled with black and white pixelated block characters (U+2591 light shade). it still works though.