twopn/site-src/spout/index.html
2026-01-24 20:55:36 -05:00

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>