647 lines
23 KiB
HTML
647 lines
23 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))};
|
|
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"));
|
|
};
|
|
</script>
|
|
</head>
|
|
<body>
|
|
<h1 style="margin: 1rem">spout: reverse-chron html pieces by <a href="https://terezi.pyrope.net">mehbark</a></h1>
|
|
|
|
<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="#s7">s7</a>: i may be stupid.
|
|
first of all, it's worth understanding that <a href="#s7">s7</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>
|