twopn/site-src/spout/index.html
2026-03-29 23:42:32 -04:00

720 lines
25 KiB
HTML

<!doctype html>
<html><head><title>spout</title>
<meta charset="UTF-8">
<meta content="spout" property="og:title" />
<meta content="i post html" property="og:description" />
<meta content="https://terezi.pyrope.net/spout" property="og:url" />
<meta content="#008282" data-react-helmet="true" name="theme-color" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<style>
html {
--fg: #ede;
--bg: #233;
--accent: #76a6e4;
color: var(--fg);
background-color: var(--bg);
}
body {
width: 100%;
height: 100%;
margin: 0;
}
a[href^="#s"], a[href^="#s"]:visited {
text-decoration: none;
font-weight: bold;
}
a, a:visited {
color: var(--accent);
text-decoration: dotted underline;
}
a:hover {
text-decoration: solid underline;
}
article {
padding: 1rem;
border-bottom: 1px solid var(--fg);
margin-inline: 2rem;
margin-bottom: 1rem;
width: min(calc(95vw - 4rem), 15cm);
}
canvas {
image-rendering: pixelated;
}
.emoticon {
font-family: "Courier New", "Courier Std", courier, monospace;
font-weight: bold;
color: #008282;
}
.controls {
display: flex;
}
.controls > button {
flex: 1;
aspect-ratio: 1;
font-size: 2rem;
font-weight: bold;
}
h1 {
font-size: 1.2em;
}
h2 {
font-size: 1.1em;
font-style: italic;
}
</style>
<script>
// pretty neat, right?
let hl_ref = () => {
if (!document.location.hash) return;
document.querySelectorAll("article").forEach(a => a.style.backgroundColor = null);
document.querySelector(document.location.hash).style.backgroundColor = "#344";
};
window.onhashchange = hl_ref;
Number.prototype.map = function(a,b,c,d){return c+(d-c)*((this-a)/(b-a))};
/**
* @type (s: number, ids: string) => Element[]
*/
let elems = (s, ids) => ids.split(" ").map(i => document.getElementById(`s${s}-${i}`));
window.onload = () => {
hl_ref();
let [s9lhs, s9rhs] = elems(9, "lhs rhs");
const params = new URLSearchParams(window.location.search);
params.get("s9-lhs") && (s9lhs.value = params.get("s9-lhs"));
params.get("s9-rhs") && (s9rhs.value = params.get("s9-rhs"));
s17();
};
function s17() {
let [canvas, lhs, rhs, button] = elems(17, "canvas lhs rhs button");
const params = new URLSearchParams(window.location.search);
params.get("s17-lhs") && (lhs.value = params.get("s17-lhs"));
params.get("s17-rhs") && (rhs.value = params.get("s17-rhs"));
/**
* @type CanvasRenderingContext2D
*/
let ctx = canvas.getContext("2d");
let width = ctx.canvas.width;
let height = ctx.canvas.height;
ctx.fillStyle = "#76a6e4";
const update = () => {
ctx.clearRect(0, 0, width, height);
const f = eval(`(x, y) => {with (Math) {return (${lhs.value}) - (${rhs.value})}}`);
for (let x = 0; x < width; x++) {
for (let y = 0; y < width; y++) {
let pos_count = 0;
for (const dx of [-0.5, 0.5]) {
for (const dy of [-0.5, 0.5]) {
const diff = f((x + dx).map(0, width, -4, 4), (y + dy).map(0, height, 4, -4));
diff > 0 && pos_count++;
}
}
if (pos_count == 1 || pos_count == 2) {
ctx.fillRect(x, y, 1, 1);
}
}
}
};
button.onclick = update;
update();
}
</script>
</head>
<body>
<h1 style="margin: 1rem">spout: reverse-chron html pieces by <a href="https://terezi.pyrope.net">mehbark</a></h1>
<article id="s17">
<h1><a href="#s17">#</a> graphing with sign-changes</h1>
<p>I had somehow never heard of this canonical graphing technique until watching <a href="https://www.youtube.com/watch?v=YJJk0iLJ4Ks">this video about a 3d graphing calculator in minecraft</a></p>
<p>just look at the signs of the difference between the left and right sides of the equation at four points for each pixel. then fill the pixel if not every sample has the same sign</p>
<canvas id="s17-canvas" width="512" height="512"></canvas>
<input id="s17-lhs" type="text" value="y" />
<span>=</span>
<input id="s17-rhs" type="text" value="sin(x**2)" />
<input id="s17-button" type="button" value="graph!">
</article>
<article id="s16">
<h1><a href="#s16">#</a> binary search over the integers (todo)</h1>
<h2 id="s16num">0</h2>
<p>low: <span id="s16low">?</span>, high: <span id="s16high">?</span></p>
</article>
<article id="s15">
<h1><a href="#s15">#</a> eva interface stuff style noodling</h1>
<div style="display: grid; background-color: black; filter: blur(0.5px) saturate(1.2); padding: 1rem; justify-items: center; align-items: center; aspect-ratio: 2/1;">
<div style="background: repeating-linear-gradient(-45deg, red, red 0.99cm, transparent 1cm, transparent 2cm, red 2.01cm); filter: blur(2px) drop-shadow(0 0 1px red); grid-area: 1/1; width: 100%; height: 100%"></div>
<div style="background-color: black; grid-area: 1/1; z-index: 1; width: 90%; height: 80%; text-align: center">
<h1 style="color: red; font-weight: 900; filter: drop-shadow(0 0 2px red); font-size: 24pt; margin: 0;">EMERGENCY</h1>
<p style="color: orange;filter: drop-shadow(0 0 2px orange);">bla bla stuff is happening i have finished the show but not eoe</p>
<svg version="1.1"
viewBox="0 0 300 100"
style="display: block; height: 2cm; margin-inline: auto; margin-top: 1.5rem;"
xmlns="http://www.w3.org/2000/svg">
<style>
.ok {
filter: drop-shadow(0 0 3px orange);
fill: orange;
}
.bad {
filter: drop-shadow(0 0 3px #fb1515);
fill: #fb1515;
}
.meh {
filter: drop-shadow(0 0 3px #e35c1a);
fill: #e35c1a;
}
</style>
<defs>
<svg id="box" viewBox="0 0 300 100">
<path d="M 0,0 h 275 l 25,25 v 75 h -275 l -25,-25 Z" />
<text x="0" y="0">yo</text>
</svg>
</defs>
<use href="#box" class="ok" width="90" height="30" x="3" y="3" />
<use href="#box" class="ok" width="90" height="30" x="3" y="36"/>
<use href="#box" class="ok" width="90" height="30" x="3" y="69"/>
<use href="#box" class="meh" width="90" height="30" x="95" y="3" />
<use href="#box" class="meh" width="90" height="30" x="95" y="36"/>
<use href="#box" class="ok" width="90" height="30" x="95" y="69"/>
<use href="#box" class="bad" width="90" height="30" x="188" y="3" />
<use href="#box" class="bad" width="90" height="30" x="188" y="36"/>
<use href="#box" class="meh" width="90" height="30" x="188" y="69"/>
</svg>
</div>
</div>
</article>
<article id="s14">
<h1><a href="#s14">#</a> fake book covers</h1>
<p><a href="https://eggbugstestplace.jcink.net/index.php?showtopic=74">chorum thread</a>, i have an idea</p>
<div id="s14-book-1">
<h1>Suturing Spectra</h1>
<h2>Case Studies in Chromatic Containment</h2>
<div class="strip"></div>
<p>Stanton Parker</p>
<p class=series>Practical Anomalous Physics: Volume One</p>
</div>
<p>The original SCP logo was designed by far2, based on <a href="https://youtube.com/watch?v=AEbUZomoBpk">a free asset</a> from <a href="https://en.wikipedia.org/wiki/Adobe_Illustrator">Adobe Illustrator</a>'s "Mad Science" asset library, which in turn was based on the <a href="https://en.wikipedia.org/wiki/File:ESD-warning-symbol.svg">electrostatic discharge warning symbol</a>. The first high-resolution PNG version of the logo was made by Aelanna, based on the original SCP logo. <a href="https://commons.wikimedia.org/wiki/File:SCP_Foundation_(emblem).svg">SCP Foundation (emblem)</a>, <a href="https://creativecommons.org/licenses/by-sa/3.0/legalcode" rel="license">CC BY-SA 3.0</a>
<style>
#s14-book-1 {
width: 6cm;
height: 7.8cm;
background: url(https://upload.wikimedia.org/wikipedia/commons/e/ec/SCP_Foundation_%28emblem%29.svg) #fffefd;
background-size: 10cm;
background-position: 2cm -0.3cm;
background-repeat: no-repeat;
color: #2c2c2c;
/* padding: 0.5cm 1cm; */
font-family: "Charter", charter, serif;
outline: 0.2cm solid black;
filter: blur(0.25px);
}
/*
#s14-book-1::before {
backdrop-filter: blur(10px);
content: "";
display: block;
position: relative;
inset: 0;
}
*/
#s14-book-1 > h1 {
font-size: 18pt;
width: 7cm;
margin-left: 0.4cm;
padding-top: 0.4cm;
margin-bottom: 0;
}
#s14-book-1 > h2 {
font-size: 13pt;
font-variant: italic;
margin-left: 0.4cm;
width: 4cm;
margin-top: 0.4cm;
margin-bottom: 0.3cm;
}
#s14-book-1 > .strip {
height: 1cm;
background: linear-gradient(to right in hsl, #f00, #0f0, #000, #0ff, #00f, #f0f);
}
#s14-book-1 > p {
margin-left: 0.4cm;
width: 2.5cm;
font-size: 10pt;
margin-top: 0.3cm;
}
#s14-book-1 > .series {
font-style: italic;
width: 2cm;
}
</style>
</article>
<article id="s13">
<h1><a href="#s13">#</a> can i embed a godot game?</h1>
<details><summary>of course</summary>
<iframe src="https://terezi.pyrope.net/s13" width=300 height=300 title="basic asteroid game"></iframe>
</article>
<article id="s12">
<h1><a href="#s12">#</a> sπgot</h1>
<h2>hey, that's almost a spout!</h2>
<p>here's a π spigot from <a href="https://www.gavalas.dev/blog/spigot-algorithms-for-pi-in-python/">this blog post</a>.
a while ago, i implemented one of the algorithms here in scheme. it was fun to get a megabyte of π, and i want you to be able to have a similar experience <span class=emoticon>:D</span>.
this version is unproven, <a href="https://www.gavalas.dev/blog/spigot-algorithms-for-pi-in-python/#the-open-problem">as discussed in the original article</a>, buuuut <abbr title="i don't really care">idrc</abbr>.
fair warning: especially with larger batch sizes, this is liable to freeze up the tab. tread carefully!
</p>
<pre id=s12-output style="overflow: auto; height: 36ch; width: 100%; word-break: break-all; white-space: pre-wrap;">
3.</pre>
<button onclick='
(() => {
if (this.interval) {
clearInterval(this.interval);
this.interval = null;
this.innerText = "sπgot!";
return;
}
this.innerText = "sτop!";
function* gen() {
let [q, r, t, i] = [1n, 180n, 60n, 2n];
while (true) {
let u = 3n*(3n*i+1n)*(3n*i+2n);
let y = (q*(27n*i-12n)+5n*r)/(5n*t);
yield y;
[q, r, t, i] = [10n*q*i*(2n*i-1n), 10n*u*(q*(5n*i-2n)+r-y*t), t*u, i+1n];
}
}
let [output, digits, batch, error] = elems(12, "output digits batch error");
let g = gen();
g.next();
this.interval = setInterval(() => {
let n = +batch.value;
let str = "";
let digs = +digits.innerText;
try {
for (let i = 0; i < n; i++) {
str += g.next().value;
digs++;
}
} catch (e) {
clearInterval(this.interval);
this.interval = null;
this.innerText = "error :[";
error.innerText = e.toString();
} finally {
output.innerText += str;
digits.innerText = digs;
}
}, 0);
})();
'>sπgot!</button> <span id=s12-digits style="font-family: monospace">1</span> digits in batches of <input id=s12-batch type=number min=1 max=100000 value=10></input> <p style="color: red" id=s12-error></span>
<p>gotta say, it's really sweet working with native generators. not really necessary here, but elegant.
bigints were the real hero, i almost discarded the idea before i remembered js had them <span class=emoticon>:P</span>
<p>at around 2<sup>15</sup> digits, the bigints become too big to allocate, at least on my machine. lame!
</article>
<article id="s11">
<h1><a href="#s11">#</a> emanresu A π approximation</h1>
<p>this is a geminic sequel to <a href="#s10">s10</a>, which does exist yet!
<p>this technique is taken from <a href="https://codegolf.stackexchange.com/questions/275564/count-squares-in-my-pi-approximation">this code golf question</a>.
i hope i can do it!
<canvas id="s11-canvas" width=300 height=300></canvas><br>
<input type=range id="s11-slider" min=0 max=12 value=0 onchange='
(() => {
let [canvas, slider, best] = elems(11, "canvas slider best");
let ctx = canvas.getContext("2d");
ctx.clearRect(0, 0, 4113, 4113);
let steps = +slider.value;
let c = (x, y) => x*x + y*y <= 1;
let inside = 0;
// why do all of the steps if we do not save work
// for colors, fool!
for (let i = steps; i >= 1; i--) {
let side_len = 2**-i;
let square_size = side_len**2;
ctx.fillStyle = `hsl(${i.map(8, 1, 0, 360)}deg, 70%, 50%)`
for (let x = 0; x < 1; x += side_len) {
for (let y = 0; y < 1; y += side_len) {
if (c(x + side_len, y)) {
ctx.fillRect(x.map(0, 1, 0, 300), y.map(0, 1, 300, 0), 300/(2**i), 300/(2**i));
if (i == steps) inside += square_size;
}
}
}
}
best.innerText = inside * 4;
})();
'/> best guess: <span id="s11-best">idk</span>
<p>i did it! not super efficiently, but it was fun. the slider goes up to 12, which is <em>really</em> pushing it, so exercise caution
</article>
<article id="s10">
<h1><a href="#s10">#</a> Monte Carlo π approximation</h1>
<p>this is a prequel to <a href="#s11">s11</a>, which does exist yet!
<p>a while ago, i made <a href="https://cohost.org/mehbark/post/5961356-mehbark-s-mini-progr">this cohost post</a> about approximating π,
and i'd like to implement my favorite approach here</p>
<canvas id="s10-canvas" width=314 height=314></canvas><br>
<input id="s10-samples" type="number" min=1 value=1000> in batches of <input id="s10-batch" type="number" min=1 value=10> <button onclick='
(() => {
let [canvas, samples, inside, total, best, batch] = elems(10, "canvas samples inside total best batch");
let n = +samples.value;
let b = +batch.value;
let [ins, tot] = [+inside.innerText, +total.innerText];
if (!(n > 0)) {
alert(`come on, ${n} samples?`);
return;
}
let ctx = canvas.getContext("2d");
setTimeout(function sample() {
for (let i = 0; i < b; i++) {
let x = Math.random();
let y = Math.random();
let win = x*x + y*y <= 1;
ctx.fillStyle = win ? "#76a6e4" : "#ede";
ctx.fillRect(x.map(0, 1, 0, 300), y.map(0, 1, 300, 0), 1, 1);
if (win) ins++;
tot++;
if (!n--) break;
}
inside.innerText = ins;
total.innerText = tot;
best.innerText = 4 * ins/tot;
if (n > 0) setTimeout(sample, 0);
}, 0);
})();
'>sample!</button><br>
<p>current best guess: <span id="s10-inside">0</span>/<span id="s10-total">0</span> * 4 = <span id="s10-best">idk</span>
<p>this is thoroughly ingrained in my brain, but i hope it's still fairly intuitive for others.
i like how the boundary becomes more clear with more samples, it makes the accuracy more understandable <abbr title="in my opinion">imo</abbr>
</article>
<article id="s9">
<h1><a href="#s9">#</a> apropos of <a href="#s7">s7</a></h1>
<p>i may not be stupid!
<p>the technique as applied in <a href="#s6">s6</a> may be pointless, but, for a general equality, points that can move in two directions could actually be interesting!
that might be here! in ! th efuture</p>
<canvas id="s9-canvas" width=300 height=300></canvas><br>
<input id="s9-lhs" value="x*x + y*y"> = <input id="s9-rhs" value="1"> <button onclick='
(() => {
if (this.innerText == "stop D:") {
clearInterval(this.interval);
this.innerText = "graph!";
return;
}
this.innerText = "stop D:";
let [lhs, rhs, canvas, samples] = elems(9, "lhs rhs canvas samples");
let ctx = canvas.getContext("2d");
let [x0,x1,y0,y1] = [-Math.PI, Math.PI, -Math.PI, Math.PI];
let f = eval(`(x, y) => {with (Math) {return abs((${lhs.value}) - (${rhs.value}))}}`);
let guys = [];
for (let x = x0; x <= x1; x += (x1 - x0)/samples.value) {
for (let y = y0; y <= y1; y += (y1 - y0)/samples.value) {
guys.push({x, y, last: 100, r: Math.random()/10, t: Math.random()*6.28});
}
}
let step = () => {
for (let guy of guys) {
let diff = f(guy.x, guy.y);
if (guy.r < 0.01) guy.r = 0.01;
if (diff > guy.last) {
guy.r *= 0.5;
guy.t += Math.random()/10;
} else {
guy.last = diff;
guy.x += guy.r*Math.cos(guy.t);
guy.y += guy.r*Math.sin(guy.t);
guy.r *= 1.1;
}
if (diff > 0.1 && Math.random() > 0.1) guy.last = diff;
}
};
let draw = () => {
ctx.fillStyle = "#76a6e4";
ctx.clearRect(0, 0, 1000, 1000);
for (let {x, y, t} of guys) {
ctx.fillStyle = `hsl(${t/6.28}turn 100% 60%)`;
ctx.fillRect(x.map(x0, x1, 0, 300), y.map(y0, y1, 300, 0), 1, 1);
}
};
draw();
// the stop button works now, so why limit steps?
let steps = 1/0;
this.interval = setInterval(function loop() {step(); draw(); if (steps-- <= 0) clearInterval(this.interval)}, 0)
})();
'>graph!</button> <input type=number id=s9-samples value=100></input> samples<sup>2</sup>
<p>okay dang this looks freaking great. hue is based on angle, look at the code! i love this
<p>i have decided that instead of making it more accurate, i will leave it at this place of looking very cool
</article>
<article id="s8">
<h1><a href="#s8">#</a> s(oko)8(an)</h1>
<p>i never got anywhere with width-hacking on cohost, but can i implement a normal game in programming language‽
<p><abbr title="work in progress">WIP</abbr>! functional! really needs undo though!</p>
<p>see <a href="http://www.sokobano.de/wiki/index.php?title=Level_format">here</a> if you want to know the #lingo</p>
<textarea id="s8-input" rows=20 style="width: 100%; padding: 0; resize: vertical">
# #
# # #
# # #
# # #
# # #
# # #
# # #
##########
#........####
# $$$$$$$# #
#.$......# #
# $$$$$$ # #
#......$+# #
#$$$$$$$ # #
# ####
##########
"Steaming Hot" by David Buchweitz</textarea>
<div class="controls"><button id="s8-l"></button><button id="s8-u"></button><button id="s8-d"></button><button id="s8-r"></button></div>
<div>winning? <span id="s8-winning">no.</span> moves? <span id="s8-moves"></span></div>
<script>
(() => {
let [input, l, u, d, r, winning, moves] = elems(8, "input l u d r winning moves");
let step = (dx, dy, c) => () => {
let puzzle = input.value.split("\n");
let max_width = puzzle.reduce((m, s) => Math.max(m, s.length), 0);
puzzle = puzzle.map(s => s.padEnd(max_width).split(""));
let in_bounds = (x, y) => 0 <= x && x < max_width && 0 <= y && y < puzzle.length;
let at = (x, y) => {
let c = in_bounds(x, y) ? puzzle[y][x] : "#";
let out = {wall: false, player: false, goal: false, box: false, c, x, y};
switch (c) {
case "*":
out.goal = true;
case "$":
out.box = true;
break;
case "+":
out.goal = true;
case "@":
out.player = true;
break;
case "#":
out.wall = true;
break;
case ".":
out.goal = true;
break;
}
return out;
};
let open = sq => !sq.wall && !sq.box;
let set = (x, y, c) => puzzle[y][x] = c;
let px, py;
for (let y = 0; y < puzzle.length; y++) {
for (let x = 0; x < max_width; x++) {
if (!at(x, y).player) continue;
px = x;
py = y;
}
}
if (typeof px == "undefined") {
winning.innerText = "THERE ISN'T A PLAYER DOOFUS";
return;
}
let [p, m] = [at(px, py), at(px + dx, py + dy)];
if (open(m)) {
set(px, py, p.goal ? "." : " ");
set(m.x, m.y, m.goal ? "+" : "@");
moves.innerText += c;
} else if (m.box) {
let m2 = at(px + 2*dx, py + 2*dy);
if (open(m2)) {
moves.innerText += c;
set(px, py, p.goal ? "." : " ");
set(m.x, m.y, m.goal ? "+" : "@");
set(m2.x, m2.y, m2.goal ? "*" : "$");
}
}
input.value = puzzle.map(r => r.join("")).join("\n");
let won = !puzzle.join().includes("$");
winning.innerText = won ? "yes!" : "no.";
};
l.onclick = step(-1, 0, "l");
u.onclick = step( 0, -1, "u");
d.onclick = step( 0, 1, "d");
r.onclick = step( 1, 0, "r");
})();
</script>
</article>
<article id="s7">
<h1><a href="#s7">#</a> i may be stupid</h1>
<p>i realized something when talking about <a href="#s6">s6</a>: i may be stupid.
first of all, it's worth understanding that <a href="#s6">s6</a> is actually my third go at this technique,
the first time was on <code>about:blank</code> in canvas, and the second time was in <a href="https://www.lexaloffle.com/picotron.php">picotron</a>.</p>
<canvas id="s7-canvas" width=300 height=300></canvas><br>
<input id="s7-input" value="sin(x)"> <button id="s7-graph" onclick='
(() => {
let ctx = document.getElementById("s7-canvas").getContext("2d");
ctx.fillStyle = "#76a6e4";
let f = eval(`x => {with (Math) {return ${document.getElementById("s7-input").value}}}`);
let deriv = f => x => (f(x + 1e-6) - f(x))/1e-6;
let order = +document.getElementById("s7-deriv").innerText;
for (let i = 0; i < order; i++) f = deriv(f);
let [x0,x1,y0,y1] = [-Math.PI, Math.PI, -Math.PI, Math.PI];
ctx.clearRect(0, 0, 1000, 1000);
for (let x = x0; x <= x1; x += (x1 - x0)/1200) {
ctx.fillRect(x.map(x0, x1, 0, 300), f(x).map(y0, y1, 300, 0), 1, 1);
};
})();
'>graph!</button> <button id="s7-deriv" onclick='
this.innerText++;
let n = +this.innerText;
document.getElementById("s7-th").innerText = n > 10 && n < 20 ? "th" : ["th", "st", "nd", "rd", ...Array(6).fill("th")][n%10];
document.getElementById("s7-graph").click();
'>0</button><span id="s7-th">th</span> derivative
<p>i just have to graph the function! the other method just looks cooler and is less reliable
<p>here's the important part of the code: <code>ctx.fillRect(x.map(x0, x1, 0, 300), f(x).map(y0, y1, 300, 0), 1, 1)</code>
<p>i also added approximated derivatives for fun <span class=emoticon>:D</span>
</article>
<article id="s6">
<h1><a href="#s6">#</a> a very fun approach to graphing</h1>
<p>now, this is hardly “production-grade,” but it is fun. first play, then talk.</p>
<canvas id="s6-canvas" width=300 height=300></canvas><br>
<input id="s6-input" value="sin(x)" onkeyup='
let ctx = document.getElementById("s6-canvas").getContext("2d");
ctx.fillStyle = "#76a6e4";
let f = eval(`(x, y) => {with (Math) {return y - (${this.value})}}`);
let [x0,x1,y0,y1] = [-Math.PI, Math.PI, -Math.PI, Math.PI];
let guys = [];
for (let x = x0; x <= x1; x += (x1 - x0)/600) {
guys.push({x, y: 0, dy: 0.1, lastDiff: 1});
};
let draw = () => {
ctx.clearRect(0, 0, 1000, 1000);
for (let guy of guys) {
ctx.fillRect(guy.x.map(x0, x1, 0, 300), guy.y.map(y0, y1, 0, 300), 1, 1);
}
};
let step = () => {
for (let guy of guys) {
let diff = Math.abs(f(guy.x, guy.y + guy.dy));
if (Math.abs(guy.dy) < 0.005) guy.dy = 0.01 * (Math.random() - 0.5);
if (diff > guy.lastDiff) {
guy.dy = -guy.dy/1.5;
} else {
guy.y += guy.dy;
guy.dy = guy.dy * 2;
guy.lastDiff = diff;
}
}
}
let total_diff = () => guys.reduce((sum, g) => sum + g.lastDiff, 0);
let delay = 100;
draw();
setTimeout(function frame() {
step();
draw();
console.log(total_diff());
if (total_diff() > 1) window.setTimeout(frame, delay);
}, delay);
'>
<p>now this is a very janky demo for many, many reasons, but there's a kernel of an idea here that's very cool: moving a bunch of things independently to do a big thing.
<p>some ideas to make this suck less:
<ul>
<li>fix the horrendous onchange behavior to actually stop the previous attempt
<li>use <a href="https://en.wikipedia.org/wiki/Newton%27s_method">Newton's method</a> with an approximated derivative
<li>tweak parameters
</ul>
<p>now look at the code!
</article>
<article id="s5">
<h1><a href="#s5">#</a> why spout</h1>
<p>i have <a href="https://b.pyrope.net">my blog</a> for when i want to make really legit-looking posts.
it's made with <a href="https://docs.racket-lang.org/pollen/">pollen</a>, which is great, and i can make even the most lame posts (currently all of the posts) look very nice.
however, there's a whole build system, the book is a program after all, a lot of things have to happen to make a post there happen. so i wanted something smaller. micro, if you will.
hence, spout! i just write some html (which, tbh, i kinda prefer to markdown) with no restrictions. ssh, edit file, save file, done. it's great! it <em>really</em> needs an RSS feed, though…
</article>
<article id="s4">
<h1><a href="#s4">#</a> oh i'm totally going to</h1>
<canvas width=20 height=20 style="outline: 1px solid var(--fg)"></canvas>
<button onclick="
let ctx = document.querySelector('#s4 > canvas').getContext('2d');
ctx.fillStyle = `rgb(${Math.random()*256}, ${Math.random()*256}, ${Math.random()*256})`;
ctx.fillRect(Math.floor(Math.random()*20), Math.floor(Math.random()*20), 1, 1);
">pixel it</button>
</article>
<article id="s3">
<h1><a href="#s3">#</a> this is, obviously, just an html page</h1>
<p>i can do <span style="color: transparent; background-clip: text; background: linear-gradient(to right, #008282, #005682); -webkit-background-clip: text;">whatever</span> i want!
heck, i could even put <button onclick="this.innerText++; document.getElementById('s3-s').innerText = 's';">1</button> javascript<span id="s3-s"></span>
</article>
<article id="s2">
<h1><a href="#s2">#</a> hello, again</h1>
<p>this is the second
<p>also easy to write!
<p>note the # links
</article>
<article id="s1">
<h1><a href="#s1">#</a> hello, spout</h1>
<p>this is the first spout post
<p>it is easy to write
<p>that is important!
</article>
<p>Unless otherwise stated, the content of this page is licensed under Creative Commons Attribution-ShareAlike 3.0 License.</p>
<p>i hope that's sufficient? you can use my stuff</p>
<!-- woo spooky secret
<article id="s0">
<h1><a href="#s0">#</a> title</h1>
</article>
-->
</body>