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.
Monospace: A JavaScript demo in 1021 bytes, winner of the demo competition
41–50 of 131 posts
Re: Monospace: A JavaScript demo in 1021 bytes, winner of the demo competition
#42I, 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.
Re: Monospace: A JavaScript demo in 1021 bytes, winner of the demo competition
#43People 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.
Re: Monospace: A JavaScript demo in 1021 bytes, winner of the demo competition
#44It was hard to get to a working demo and here it is: http://www.p01.org/MONOSPACE/monospace-compatible.htm
Pretty interesting that even though I mute the tab it still speaks. The music does get muted, but not the TTS
Re: Monospace: A JavaScript demo in 1021 bytes, winner of the demo competition
#45People 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 lea…
Re: Monospace: A JavaScript demo in 1021 bytes, winner of the demo competition
#46The 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.
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.
Re: Monospace: A JavaScript demo in 1021 bytes, winner of the demo competition
#47Dwitter 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.
Re: Monospace: A JavaScript demo in 1021 bytes, winner of the demo competition
#48Can 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
Re: Monospace: A JavaScript demo in 1021 bytes, winner of the demo competition
#49Amazing! 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?
// 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.