130 comments

[ 3.8 ms ] story [ 210 ms ] thread
It was hard to get to a working demo and here it is: http://www.p01.org/MONOSPACE/monospace-compatible.htm
Even as a Firefox-user, I would recommend running this in Chrome.

It's clearly optimized for that target, and it's getting a much higher FPS in Chrome than Firefox.

(comment deleted)
Not optimized, Blink/WebKit doesn't have the Firefox performance issue emphasized in the article:

> Unfortunately, Mozilla still hasn't fixed the performance bug 1190398 around ShadowBlur, filled for BLCK4777 5 years ago

Jesus I should have read this comment first.

It completely locked up Firefox on Ubuntu 20.04, rendering a frame and making a single beep sound about once per second. Had to close the browser and force kill some processes to get the TTS to stop.

I had a beeping issue on Firefox 81 running on Windows 10. The weird thing was, the beeping noise continued even after I closed Firefox. There were some OS updates pending installation, so I just chose to reboot/install instead of troubleshoot further.
Works fine in Safari also
Pretty interesting that even though I mute the tab it still speaks. The music does get muted, but not the TTS
Even when the tab is removed the music continues :/
That is so weird - same here, sound keeps playing ~1-2 min after tab is closed. Must be bug in Firefox? How does that happen?
Pretty easy to not properly clean up resources when you share processes between tabs. Firefox does that, chrome doesn't, so it properly cleans up in chrome since it quits the process.

If you wonder why it plays for 1-2 minutes after the code stopped sending to the sound card, you always write sound in batches to the sound card so he likely wrote the whole 1-2 minutes in one big batch.

Makes sense!
Tanked my firefox instance, which I closed, and the noise from the page continued. Do no open this in firefox.
(comment deleted)
And when I tried to run it in Chrome, my laptop (MacBook Air 2015 early) completely froze up after a few seconds. Probably you should not open this in older devices at all.
It runs fine on my mid-2014 Macbook Pro. It's not related to the age of the device.
runs fine in safari on a 2013 Mac.
> Unfortunately, Mozilla still hasn't fixed the performance bug 1190398 around ShadowBlur, filled for BLCK4777 5 years ago.

> Therefore you need to use a Chromium or Webkit browser to enjoy MONOSPACE live.

It did stutter a little bit but I could enjoy it with Chrome on my MacBook Pro 2012.
Because the rest of the commentors in this thread seem to not be reading the article, there's a performance issue in Firefox due to the following unresolved bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1190398
The link to the demo comes before the warning to firefox, don't shame commenters who chose to see the content at discussion first before reading below the fold.
Who's shaming anyone?
Guess . . . . "Because the rest of the commentors in this thread seem to not be reading the article"
We'll, they're not reading the article, yet they're expecting people to read their comment. And if not, why comment at all?

I think some selective derision is warranted.

While it says that "[...] you need to use a Chromium or Webkit browser to enjoy MONOSPACE live." (emphasis mine), it does not say "DO NOT USE FIREFOX UNDER ANY CIRCUMSTANCES, it will mess up your device for minutes" anywhere.

We saw so many (warranted or unwarranted) "Best viewed with Chrome"-statements on the web already that we became desensitized to it years ago.

No, please don't add derision to HN threads. When someone else is wrong, it's enough to supply correct information. That enhances the thread without damaging the commons.

https://news.ycombinator.com/newsguidelines.html

Are you saying everything everyone says is above critique, or that derision specifically is not welcome?
The latter. It's never needed and does more harm than good.
I wonder what exactly is Firefox dev team spending all that time saved by not fixing peformance issues such as the bug linked above.

Chrome seems to be doing some things right and Firefox is just adrift...

I was so happy not to see another cheap dismissal here.

Too often you have to read how a sizecoded demo uses a ton of system libraries or whatever, completely dismissing the technical achievement.

That's a nonsense statement. There is no technical achievement, it's no breakthrough in any relevant technical sense. Sorry to get technical, but you called it.

I'm not dismissing the demo, mind. It's a great feat of engineering and its main achievement, that is creative, surely thrives from a technical esthetic.

Using system libraries has to be put into perspective, for sure. The common wisdom (in sizecoding at least) is that creativity thrives from constraints. Necessarily then, one has to be aware of the constraints.

One might say having to write in .js is extreme masochism, so the criticizm about .dlls is just one side of the equation. Browser bugs and all that. no, thanks.

To everyone running it in Firefox: "Unfortunately, Mozilla still hasn't fixed the performance bug 1190398 around ShadowBlur, filled for BLCK4777 5 years ago.

Therefore you need to use a Chromium or Webkit browser to enjoy MONOSPACE live."

There is no opportunity to read this information before . . .
Don't try this [1] in Firefox, from the article:

> Unfortunately, Mozilla still hasn't fixed the performance bug 1190398 around ShadowBlur, filled for BLCK4777 5 years ago

> Therefore you need to use a Chromium or Webkit browser to enjoy MONOSPACE live

[1] http://www.p01.org/MONOSPACE/monospace-compatible.htm

For people not able (or willing) to run the demo themselves, it can also be seen on YouTube in a 3 minute clip [0]. It keeps amazing me how much people can accomplish with less than a KB of code.

[0] https://www.youtube.com/watch?v=2HN139WkcCY

Not being willfully obtuse, but there's a lot more than a KB of code on that YouTube page. To me it feels like the difference between watching a movie on 35mm vs DVD.
I expected the comment to go on about how much code in the support libraries is needed to support this. It's not a 512 byte boot sector intro, far from it.
The video was lost on me whereas the demo made me think how cool it was.

I expected the video to explain what it was but alas no. The video is just the demo running. The demo was brilliant, but, the video of the demo, although practically the same had me wondering quite negative thoughts. I wasn't thinking 'how do they do that', I was thinking 'this is a bit pretentious'. It is amazing the difference context makes.

Totally agree. I can't watch the demo run because I'm on Firefox, and I have no desire to watch a video for the reasons you described. And the world keeps spinning, I guess.
> I wasn't thinking 'how do they do that', I was thinking 'this is a bit pretentious'.

Everyone experiences things differently. Some people see a sports car and think, "Wow, I wonder how it works?" Others, "Wow, I hate that colour." Doesn't really matter.

(comment deleted)
Don't use this piece of JS shit in Firefox!
I'm getting the same issues as all of the other Firefox users. Guess I'll never watch it, because I have a hard policy of never, ever installing Chrome malware on my personal computer.

Maybe the title should be changed to "Chrome demo"?

Jesus, no one cares what your "policy" is.

Not to mention, it's a Firefox bug, so probably should be changed to "Firefox bug demo".

I fucking hate how snarky sometimes HN can be.

> Jesus, no one cares what your "policy" is.

I care.

Just watch the attached video?...
Maybe the title should be changed to "Chrome demo"?

It works in Chromium browsers (Chrome, Edge) and Webkit browsers (Safari). Saying it's a Chrome demo would not be accurate.

Works pretty well on Safari
You could ask one of your friends to watch the demo on their computer.
Who knows, maybe the policy extends as far as not having any friends using Chrome.
I, for one, liked the "firefox version" of the demo. No way to close, mute or go back. And after a minute someone starts talking to you. Pretty cool.
Yeah Firefox Mobile also has some serious issues with that.it was fun though ;)
No problem. The beauty about open source software is that the code is freely available, so you can fix bugs yourself. /sarc
This is amazingly impressive. It boggles my mind how much can be done with so little.
Amazing! Very impressive how you got all the details in. I'd be interested in hearing what the development process is like.

Do you work directly in the minified code, or do you create the demo in normal code first then look for ways to minimize it?

I'm not the author but I've done this before [1], so here's what I can quickly make up (EDIT: updated using informations gathered from p01's comment below):

    // c is a canvas created outside
    d = [ // 2 times audio frequencies used, I think
      2280,
      1280,
      1520,
      c.width = 1920,
      // d[4] is not used, not sure why this stmt was stuffed into d
      // required to hide the PNG bootstrap; the bare minimum would be `0'`, probably this compresses better though?
      document.body.style.font = "0px MONOSPACE"
    ],
    g = new AudioContext,
    o = g.createScriptProcessor(4096,
                                // clears the margin and initializes vars
                                // (t: time in seconds, n: last t when speak occurred)
                                document.body.style.margin = t = n = 0,
                                1),
    o.connect(g.destination),
    o.onaudioprocess = o => { // periodically called to fill the audio buffer, used in place of setInterval
      o = o.outputBuffer.getChannelData(
        e = Math.sin(
          t / 16 % 1, // this is the only arg to sin, others are for shoving exprs into a single stmt
          m = Math.sin(Math.min(1, y = t / 128) * Math.PI) ** .5 + .1,
          c.height = 1080, // setting canvas.width/height clears the canvas
          b.shadowOffsetY = 32420,
          // results in `radial-gradient(#222,black` or so, reinterpreting decimal number as hex, the last `)` is not required
          c.style.background = "radial-gradient(#" + [222, 222, 222, 222, 155, 155, 102, 102][t / 16 & 7] + ",black",
          b.font = "920 32px MONOSPACE",
          // each function determines the dot size for 16 seconds, also sometimes used as a display text
          f = [
            (x, y, t) => x / y * 2 - t,
            (x, y, t) => (x ** 2 + y ** 2) ** .5 - t,
            (x, y, t) => x / 4 ^ y / 4 - t,
            (x, y, t) => y % x - t
          ][t / 16 & 3],
          // determines a string to print and speaks it every 16 second
          // the inner [...][t/16|0] can return undefined, which gets coerced to an empty string by `""+[...]`
          u = "" + [[, f, f, " CAN YOU HEAR ME", f, f, , "MONOSPACE", "THE END"][t / 16 | 0]],
          t > n && speechSynthesis.speak(new SpeechSynthesisUtterance(u, n += 16)))
      );
      for (i = 0; 4096 > 4 * i; i++) // for each dot; `4096>4*i` probably compresses better than `1024>i`
        // calculate the dot size and mix with the radius in the previous frame for easing
        // f and g are objects (function and AudioContext), so can be abused as a generic store
        g[i] = r = (f(x = 16 - i % 32, a = 16 - (i / 32 | 0), t) / 2 & 1) + (g[i] || 0) / 2,
        x += o[0] / 4 + 4 * (1 - m ** .3) * Math.sin(i + t + 8),
        a += o[64] / 4 + 4 * (1 - m ** .3) * Math.sin(i + t),
        h = x * Math.sin(y * 2 + 8) + a * Math.sin(y * 2),
        p = 4096 / (m * 32 + 4 * h * Math.sin(e) + t % 16),
        b.beginPath(f[i] = r / p),
        b.arc(h * Math.sin(e + 8) * p + 1280,
              x * Math.sin(y * 2) * p - a * Math.sin(y * 2 + 8) * p - 31920,
              p > 0 && p / (2 + 32 - r * 16),
              0,
              8), // anything larger than `2*Math.PI` will draw a full circle
        b.shadowBlur = o[0] ** 2 * 32 + 32 - m * 32 + 4 + h * h / 2,
        // `[a,b,c]` coerces into a string `a,b,c`
        b.shadowColor = "hsl(" + [f(x, y, t) & 2 ? t - a * 8 : 180, (t & 64) * m + "%", (t & 64) * m + "%"],
        b.fill();
      b.shadowBlur = o[0] ** 2 * 32,
      b.shadowColor = "#fee&q...
Thanks for the expansion and comments! On your project, I noticed the comments were about 5 times longer than the source code :) - so that makes sense how to work with it.
Hi there.

I do not use any minifier. I minify the code by hand, and typically prototype ideas and performance tests in normal non-minified code. Once the main idea and approach are settled, I minify the code by hand and keep an eye on the heatmap of the DEFLATE stream to match the 1024 bytes limit.

MONOSPACE took ~4months on an off to create, tallying ~60h of work. You know 2020 + trying to balance work & family, and remain sane these days.

As I said on my site, the Audio and Visuals feed each other to make the background noise based on each dot, and the camera shake based on the Audio. That way I keep the Audio & Visuals are perfectly in synch, for free ;)

For the X & Y camera shake, I use the values 0 and 64 from the Audio buffer. The 64 is because this is the maximum number of characters rendered by the Text writer which happens in the loop updating the Audio buffer. Using something lower than the 64th value would make the last characters of the Text shake in a different way than the rest.

-- P01, author of MONOSPACE,

Awesome work!

That makes sense. I imagine with some experience you have a decent idea about what you can make fit.

> tallying ~60h of work

This is far less than I expected, which usually means I am hopelessly out of my league in the topic. Great job :)

That is 60h for this project alone.

I made 100s of creative projects and failed experiences before getting there. Don't get scared by that number. All it means, is that it is possible to do in 60h. Some would take 600, others 20.

I threw that number away to put it in context with the 4 months since the first prototype. I could only work from time to time. Some times not touching the code at all for weeks.

People love to bash developers who put up "Chrome recommended" banners, dismissing them as lazy, but when making games and such there are real issues that are impossible to circumvent. This bug is a prime example of that.
I sympathize with developers dealing with browser bugs, but a majority of "Chrome recommended" banners are not. For many cases they are used as a cheap excuse not to care about or even deter non-Chrome browsers at all, even when they do work mostly or completely fine. Demos, in comparison, tend to have much narrower requirements due to various constraints (many D3D demos only worked with specific graphic cards at least in the initial release, for example).
Now CypressJS has a Firefox backend it's less forgiveable.
I agree but the last time the pitchforks went out on HN regarding this was for a game, so not very different from demos.

The issue I'm personally dealing with is for a game revolving around a feature that's only working properly in Chrome (see sibling comment for link to bugzilla). I've tried solutions for mitigating it but since I'm just one guy I've basically given up for now and have resorted to a warning for Firefox users.

In all honesty I don't see how it's different from only releasing a game on Xbox and not PlayStation.

I guess that was shapez.io [1]? The situation around WebRender is pretty unfortunate, as AFAIK it is blocked by the compatibility issue in macOS. I have played it in Windows Firefox just fine though.

Anyway, I think it's more about the wording: it's very fine that the developer hasn't tested much in other browsers (not every developer has resources to do so, sure), but saying a certain browser is required or recommended implies that the issue is something to do with that browser (which might or might not be the case). Shapez.io also doesn't support macOS in its standalone build, but the statement on that was more amicable and honest; I wish the developer did the same for non-Chrome browsers as well.

> In all honesty I don't see how it's different from only releasing a game on Xbox and not PlayStation.

Consoles generally have much different requirements (hardware, vendor integration etc.) than PC games. For me it's more like Steam vs. Epic Store which I found pretty frustrating as a gamer.

[1] https://news.ycombinator.com/item?id=24678720

The point is that if shadowBlur performed poorly in Chrome, people would either find a way to circumvent it, or wouldn't use it. Chrome gets a free pass for its failings, while Firefox attracts "Chrome Preferred" banners, and this is based on market share rather than software quality.
> or wouldn't use it

I'm dealing with a similar issue in Firefox (https://bugzilla.mozilla.org/show_bug.cgi?id=925025) and there's simply no way to do what I want without using a blur filter. So yes, if it didn't work in Chrome my game wouldn't exist but not sure how that is of any comfort to anyone.

That sucks! From my knowledge of browser-based games, though, I think a lot of people are using webgl, where the solution would be to write a blur shader. If your game was written in a world where Firefox had 90% market share, you'd be more likely to go straight for webgl.
For many aspects of web development you're absolutely right, but don't forget that this is a demo. It's a showcase of talent rather than an attempt to write something accessible in every browser.

People have shown demos at parties that require a specific graphics card and a specific build of a driver in the past; something that requires a specific class of browser engine is probably OK.

Here are other visual JS Demos created by p01 and others using just 140 characters of code (or less!): https://www.dwitter.net/top/year

Dwitter is a collection of amazing short JavaScript codes that can be explored, remixed and voted "awesome". It works like a living portfolio of short code/graphic creations where one can learn by exploring other people's code.

It is amazing how much one can achieve with so little code. I would dare to say that such demos are a kind of...indie art?

Can you give us some pointers, which APIs and algorithms did you use to generate the demo? I bet it will be an interesting lesson on math and web

I was put off by the first paragraph. The author could have found a more modest way to introduce the subject.
The real version of this, which is actually 1021 bytes, is here: http://www.p01.org/MONOSPACE/monospace.htm. It uses the very clever trick of compressing the JavaScript code as the pixels of a PNG image (which uses Deflate internally), then including the page itself in an <img> tag and decompressing the JS code from the resulting pixels.

Unfortunately, the developer made a bit of a mistake when uploading this file: it appears to have been uploaded in ASCII mode rather than binary mode, meaning that all the 0x0d (CR) bytes in the file have gone missing (an artifact of the standard "CRLF -> LF" translation performed in text mode uploads). Therefore, it doesn't work: all you get to see is a screenful of binary junk. (Author, if you're seeing this - it's hopefully an easy fix!)

I've uploaded the correct version (obtained from http://www.p01.org/MONOSPACE/monospace.zip) here: https://robertxiao.ca/misc/monospace.htm. This works out-of-the-box on Firefox (modulo being really slow, thanks to https://bugzilla.mozilla.org/show_bug.cgi?id=1190398), but fails on newer Chrome because of click-to-play audio. You'll have to "allow" my site under chrome://settings/content/sound, then refresh.

But! Once you've jumped through those hoops, you can appreciate the real ingenuity that manages to pack this demo into a cool 1021 bytes (the compatible version that most of you would have seen is much larger, at 2338 bytes).

Ah, I wondered why I was getting junk when I clicked that link, thank you for uploading (still falls over for me when I accidentally opened it using FF, but no harm done)
Hi, my bad. I forgot to set the Transfert Type to binary on my new laptop :p

Hope you enjoy MONOSPACE anyhow. I am pretty happy with how it turned out. I believe I managed to nudge a little the state of art of what type of camera work is possible in 1kb demos.

It's absolutely beautiful, just enormously impressive. Thank you for making it!
> but fails on newer Chrome because of click-to-play audio.

In these cases, you can also refresh and rapid click, it gets registered as a user gesture and the audio is allowed to start. Maybe try two-three times.

Not sure why, but even your fixed version is showing up as binary junk for me. Firefox 78.3.1esr on Windows x86_64.
The sounds reminds me a bit of Laurie Anderson's "O Superman".
OK, this is really weird, but I closed the browser tab with the demo ca. 1 minute ago and can still hear the music. I'm on FF/Big Sur.
Same, there is a weird sine wave with high pitch and frequency.
With FF mobile on Android I still had ongoing sound after closing FF completely (at least as far as I know) and I had to reboot my phone.

It's quite concerning that websites are able to do this. While it is the browsers fault to support all that javascript crap I think this is also a bug in Android.

> It's quite concerning that websites are able to do this.

They've been able to do this for a long time and for valid reasons (think of a PWA audio player, like a podcast app).

Unfortunately this used to get abused very often. I remember seeing bugs in ads that would: stop your Spotify playback (due to how audio apis work on phones), and continue playing in background (again, this is a feature, but often abused). iOS is much better in that regard, with stricter autoplay policies, etc...

What's super concerning is that audio worked even when the page was closed. Once I kill the tab, the entire process should be killed too.

So yeah, it's possible that it's just an FF bug purely because being able to play audio in BG is a valid feature BUT there should be no audio when the tab has been killed. My guess is that from the OS perspective, the tab is still running.

My another guess is that it has something to do with some clever techniques regarding reducing the app size.

Did you try "Force Stop" newbie rebooting? I'd think that should work
I imagine that accidental unfair scheduling is to blame here: that the audio processor is keeping things busy in such a way that Firefox isn’t getting its “hey, this tab is closed” message through to kill the event loop. This is definitely a bug in Firefox, and it appears to be consistent across platforms (I’m on Windows, you’re on macOS, someone else is mentioning Android). The patch to fix this is likely to be quite small.
I do notice, that PulseAudio shows me that the audio stream from Firefox is labelled "AudioCallbackDriver", which is unusual: most Firefox audio streams are labelled "AudioStream". I think this is a little-tested feature in Firefox at this point. Somehow it also manages to activate the speech (espeak) dispatcher too.

Not cool.

Also corresponding process eats more than 1 Gb of memory.
Can confirm on latest FireFox on Mojave. It went away when I opened and closed a new blank tab.
Same on FF/Catalina, only 10-15 seconds though.
Function call without parentheses

    c.getContext`2d`
invoked as tagged template
This made me think of THX 1138, the 1971 science fiction movie by George Lucas. Very nice work!
This is similar (but much much better) to a demo I was making for JS1K a bunch of years back: http://mrspeaker.github.io/js1k16/

My "big idea" was to use the speech synthesis API (I still liked how I used it)... but I thought it was funny, because it felt like cheating to say "1K" when it relied on so many megabytes of APIs!

Does it count as megabytes when it's bog standard API ?
Nah, of course not! It's fair game (otherwise you'd first have to invent the universe). It was just the speech synth API was pretty new when I did that, so it felt sneaky ;)

Also - p01, you are a hero to us all. MONOSPACE is beautiful, congrats on the Assembly win!

The speech synthesis sounds like it is panicking. Quite funny.
This is some next-level rap music, nice.