Not cool.
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.
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.
It's clearly optimized for that target, and it's getting a much higher FPS in Chrome than Firefox.
> Unfortunately, Mozilla still hasn't fixed the performance bug 1190398 around ShadowBlur, filled for BLCK4777 5 years ago
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.
Maybe the title should be changed to "Chrome demo"?
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.
I care.
It works in Chromium browsers (Chrome, Edge) and Webkit browsers (Safari). Saying it's a Chrome demo would not be accurate.
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.
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.
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.
Chrome seems to be doing some things right and Firefox is just adrift...
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!
Also - p01, you are a hero to us all. MONOSPACE is beautiful, congrats on the Assembly win!
> Therefore you need to use a Chromium or Webkit browser to enjoy MONOSPACE live.
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).
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.
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.
-- p01, author of MONOSPACE
The main part of the source code is the last `for(...)` loop that updates the sound buffer, and draws the text.
There was not much inspiration :p I needed something simple enough to be fairly compact and sound melodic.
So I used a chord of 4 notes, played them at different speed and octaves. The lead using a sinus oscillator to get nice pure tones, a deeper sound 4x slower with a pulse oscillator to get more edgy sound, the deepest sound 16x slower with a sawtooth to get a gritty bass. Layered with the perspective scaling of the dots to get something bleeps in synch with the visuals.
Hope that answers your question
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?
// 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";
for (i = 0; 4096 > i; i++) // generate each sample, also prints the glitched text
o[i] = o[i] / 2 + (
(
Math.sin(t * d[t / [4, 4, 4, 4, 1/4, 1/4, 16, 4][t / 16 & 7] & 3] * Math.PI) * 8 +
(t * d[t / 8 & 3] / 2 & 6) + t * d[t / 16 & 3] / 4 % 6
) / 64 + f[i / 4 | 0] // f[0..1023] is the visual data, reused as a noise
) * m,
// prints at most 64 characters of u;
// 0th and 64th samples (o[0] & o[64]) of the prev/current buffer act as x/y jitter,
// first 64 samples also displaces the char offset for the glitched text effect
64 > i & t % 16 * 6 > i &&
b.fillText([u[i + (o[i] * 2 & 1)]], // again, [undefined] coerces into an empty string
i % 9 * 32 + o[0] * 16 + 180,
(i / 9 | 0) * 64 + o[64] * 16 - t - 31920),
t += 1 / g.sampleRate // so t increments by 1 per second
}
While the obfuscation itself is fairly standard, I think the real magic here is the carefully selected motion and jitters---which I can't easily figure out from a glance.> 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?
Also, in my experience you end up structuring everything so that it can be easily minifiable (by hand or using something like terser-online [2]). This doesn't necessarily mean that the code is unreadable (variables can be renamed, statements can be converted to comma expressions and so on), but the resulting code would be very unorthodox. See the source code of my JS1024 entry for example.
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.
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.
Therefore you need to use a Chromium or Webkit browser to enjoy MONOSPACE live."
Too often you have to read how a sizecoded demo uses a ton of system libraries or whatever, completely dismissing the technical achievement.
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.
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.
I appreciate they're entirely different beasts, but the Monospace demo here definitely IS creative.
> 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
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
c.getContext`2d`
invoked as tagged templateI love that people keep pushing against the boundaries of what is possible to do. It's important work, and so fun!
Ha, that's amazing.
FTFY
These modern demos, using OpenGL and now the whole web browser that are mimicking the rules and sentiment of old 8-bit machines demos, or even later DOS demos, are not the same thing.
Oh, this demo has text being spoken ... how was it done ... checks source code ... SpeechSynthesisUtterance . Yeah right, 1021 bytes. :)
I turned it off immediately after hearing its audio.
If you want folks to watch a video, allow me to suggest making it as pleasant an experience as possible, at least in terms of the audio.
Terrible marketing = marketing that makes your customers/prospects/audience suffer
[1] https://www.youtube.com/watch?v=2HN139WkcCY&feature=emb_logo