change things, images
BIN
assets/images/FTL.jpg
Normal file
After Width: | Height: | Size: 6.9 KiB |
BIN
assets/images/alphacol.jpg
Normal file
After Width: | Height: | Size: 22 KiB |
BIN
assets/images/apioform.jpg
Normal file
After Width: | Height: | Size: 18 KiB |
BIN
assets/images/csproblem.jpg
Normal file
After Width: | Height: | Size: 13 KiB |
BIN
assets/images/emu-war.jpg
Normal file
After Width: | Height: | Size: 11 KiB |
BIN
assets/images/flight.jpg
Normal file
After Width: | Height: | Size: 5.4 KiB |
BIN
assets/images/fractalart.jpg
Normal file
After Width: | Height: | Size: 22 KiB |
BIN
assets/images/gol.jpg
Normal file
After Width: | Height: | Size: 12 KiB |
BIN
assets/images/guihacker.jpg
Normal file
After Width: | Height: | Size: 8.8 KiB |
BIN
assets/images/heavscp.jpg
Normal file
After Width: | Height: | Size: 4.8 KiB |
BIN
assets/images/ideas.jpg
Normal file
After Width: | Height: | Size: 11 KiB |
BIN
assets/images/incdec.jpg
Normal file
After Width: | Height: | Size: 8.9 KiB |
BIN
assets/images/infipage.jpg
Normal file
After Width: | Height: | Size: 5.6 KiB |
BIN
assets/images/joe.jpg
Normal file
After Width: | Height: | Size: 8.8 KiB |
BIN
assets/images/lorem.jpg
Normal file
After Width: | Height: | Size: 14 KiB |
BIN
assets/images/nemc.jpg
Normal file
After Width: | Height: | Size: 10 KiB |
BIN
assets/images/new-website.jpg
Normal file
After Width: | Height: | Size: 14 KiB |
BIN
assets/images/osbill.jpg
Normal file
After Width: | Height: | Size: 6.3 KiB |
BIN
assets/images/osmarkscalculator.jpg
Normal file
After Width: | Height: | Size: 14 KiB |
BIN
assets/images/otherstuff.jpg
Normal file
After Width: | Height: | Size: 18 KiB |
BIN
assets/images/phones.jpg
Normal file
After Width: | Height: | Size: 16 KiB |
BIN
assets/images/points.jpg
Normal file
After Width: | Height: | Size: 6.3 KiB |
BIN
assets/images/rpncalc2.jpg
Normal file
After Width: | Height: | Size: 10 KiB |
BIN
assets/images/rpncalc3.jpg
Normal file
After Width: | Height: | Size: 5.0 KiB |
BIN
assets/images/rpncalc4.jpg
Normal file
After Width: | Height: | Size: 9.8 KiB |
BIN
assets/images/rssgood.jpg
Normal file
After Width: | Height: | Size: 8.7 KiB |
BIN
assets/images/scorer.jpg
Normal file
After Width: | Height: | Size: 13 KiB |
BIN
assets/images/stack.jpg
Normal file
After Width: | Height: | Size: 9.8 KiB |
BIN
assets/images/themes.jpg
Normal file
After Width: | Height: | Size: 11 KiB |
BIN
assets/images/tictactoe.jpg
Normal file
After Width: | Height: | Size: 9.9 KiB |
BIN
assets/images/whorl.jpg
Normal file
After Width: | Height: | Size: 10 KiB |
BIN
assets/images/wsterm.jpg
Normal file
After Width: | Height: | Size: 38 KiB |
@ -81,6 +81,7 @@ function jargon() {
|
||||
} else {
|
||||
var raw = thing + " " + choose(jargonWords.participles)
|
||||
.replace("writing", "wrote")
|
||||
.replace("breaking", "broken")
|
||||
.replace("overriding", "overriden")
|
||||
.replace("shutting", "shut")
|
||||
.replace("ying", "ied")
|
||||
|
@ -71,7 +71,7 @@ const getResponse = async req => {
|
||||
}
|
||||
try {
|
||||
console.log("Requesting", req.url)
|
||||
const response = await fetchWithTimeout(req.clone(), 5000)
|
||||
const response = await fetchWithTimeout(req.clone(), 10000)
|
||||
if (response.status < 400) {
|
||||
console.log("Caching request to", req.url)
|
||||
cache.put(req, response.clone())
|
||||
|
@ -38,7 +38,8 @@ Obviously this is just stuff *I* like; you might not like it, which isn't really
|
||||
* [Schlock Mercenary](https://www.schlockmercenary.com/), a *very* long-running space opera webcomic. It's been running for something like 20 years, and the art and such improve over time.
|
||||
* [Freefall](http://freefall.purrsia.com/), a hard-science-fiction webcomic.
|
||||
* [Mage Errant](https://www.goodreads.com/series/252085-mage-errant) - a moderately-long-by-now fantasy series with a very vibrant world, and which actually considers the geopolitical implications of there being beings around ("Great Powers") able to act as one-man armies.
|
||||
* [Arcane Ascension](https://www.goodreads.com/series/201441-arcane-ascension) - fun progression fantasy series with (... like most of these, actually) worldbuilding I like and good characters. I have only read the first two, since I'm writing this just as the third came out
|
||||
* [Arcane Ascension](https://www.goodreads.com/series/201441-arcane-ascension) - fun progression fantasy series with (... like most of these, actually) worldbuilding I like and good characters. I have only read the first two, since I got distracted and have not read much of the third. Somewhat overly long at times.
|
||||
* [Void Star](https://www.goodreads.com/book/show/29939057-void-star) - somewhat weird and good. The prose is very... poetic is probably the best word (it contains phrases like "isoclines of commitment and dread", "concentric and innumerable" and "high empyrean")... which I enjoyed, but it is polarizing. The setting seems like a generally reasonable extrapolation of a bunch of ongoing trends into the future, although it's unclear exactly *when* it is (some of the book implies 2150 or so, but this seems implausible). Its most interesting characteristic is that it absolutely does not tell you what's going on ever: an interview I read said it was written out of order, and that makes sense (another fun quirk of it is that the chapters are generally very short). I think I know most of what happens now, but it has taken a while.
|
||||
|
||||
Special mentions (i.e. "I haven't gotten around to reading these but they are well-reviewed and sound interesting") to:
|
||||
* [The Divine Cities](https://www.goodreads.com/series/159695-the-divine-cities) by Robert Jackson Bennet.
|
||||
|
29
blog/rssgood.md
Normal file
@ -0,0 +1,29 @@
|
||||
---
|
||||
title: "RSS: good and useful"
|
||||
description: RSS/Atom are protocols for Internet-based newsletter/feed services. They're surprisingly well-supported and you should consider using them.
|
||||
created: 14/05/2022
|
||||
---
|
||||
RSS stands for Really Simple Syndication, and it's an underappreciated protocol for generally "following" things on the internet.
|
||||
Most people do this via proprietary platforms with feed/notification functionality, the problems of which are obvious, or email.
|
||||
Email, though, is push-based - you subscribe to a service and it communicates with your email server whenever a new item is published.
|
||||
While this allows new content to be received in near-real-time, it has the significant disadvantage that unsubscription can be difficult and nonstandardized, and your address can be used by anyone else to send you unwanted mails.
|
||||
|
||||
RSS inverts this; an RSS reader application periodically checks a list of RSS feeds by downloading them from their servers and displays all new content it finds.
|
||||
This makes it a lot easier to manage a lot of feeds or items as an end user, particularly since lots of reader software will also let you categorize feeds to better manage content.
|
||||
It's also easier for site admins: because of rampant spam running your own email server (without email from it being immediately discarded) is tricky, so it's generally required to integrate some external, paid service instead.
|
||||
RSS only requires serving an XML file, which is very easy to do on top of an existing website, which is probably why it's still pretty widely implemented.
|
||||
|
||||
Yes, despite RSS's relative lack of use nowadays, a surprisingly large amount of sites still support it (some might use Atom, a slightly different protocol, but good reader applications support both transparently):
|
||||
|
||||
* WordPress, a very popular platform for blogs, has RSS support enabled by default (just go to `/feed/`).
|
||||
* YouTube has RSS feeds for channels' videos: `https://www.youtube.com/feeds/videos.xml?channel_id=[ID of channel to follow]`.
|
||||
* Some web fiction sites (e.g. Royal Road, Archive Of Our Own) have per-story RSS feeds.
|
||||
* osmarks.net has an RSS feed, linked on the main page somewhere: [https://osmarks.net/rss.xml](https://osmarks.net/rss.xml) - this does only cover blog posts and not experiments, as those aren't actually timestamped.
|
||||
* Blogspot blogs have feeds at `/rss.xml`.
|
||||
* The BBC has RSS feeds described here: [https://www.bbc.com/news/10628494](https://www.bbc.com/news/10628494).
|
||||
* Otherwise, you can ctrl+F for "RSS" or "Atom" or "feed" or "subscribe" and might be successful, or try URLs like `/feed`, `/feed.xml`, `/feed.atom`, `/index.xml`, `/rss` or `/rss.xml`.
|
||||
|
||||
As for RSS readers to use these with, there are many implementations available.
|
||||
I use [Miniflux](https://miniflux.app/), since it's self-hosted and accessible on multiple devices via the web, and has nice features like keyboard controls, scraping websites which omit some content from RSS feeds, and an integration API which I use to plug it into my convoluted mess of custom scripting.
|
||||
The [Awesome Self-Hosted list](https://github.com/awesome-selfhosted/awesome-selfhosted#feed-readers) has many other reader applications like this.
|
||||
If you prefer something which runs locally as a desktop application, [Wikipedia has a list](https://en.wikipedia.org/wiki/Comparison_of_feed_aggregators) (I haven't actually checked this space myself).
|
240
experiments/flight/index.html
Normal file
@ -0,0 +1,240 @@
|
||||
---
|
||||
title: Flying Thing
|
||||
description: Fly an ominous flying square around above some ground! Includes special relativity!
|
||||
---
|
||||
<style>
|
||||
body {
|
||||
box-sizing: border-box;
|
||||
font-family: 'Fira Sans', 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||||
}
|
||||
|
||||
#controls {
|
||||
border: 1px solid blue;
|
||||
padding: 1em;
|
||||
margin: 1em;
|
||||
}
|
||||
#controls select {
|
||||
border: 1px solid blue;
|
||||
padding: 0.2em;
|
||||
}
|
||||
#container {
|
||||
display: flex;
|
||||
}
|
||||
</style>
|
||||
<div id="container">
|
||||
<canvas id="thing" width=800 height=800></canvas>
|
||||
<div id="controls-container">
|
||||
<div id="controls">
|
||||
<label><select name="mechanics"><option>Relativistic</option><option>Newtonian</option><option>Aristotlean</option></select> Mechanics</label><br>
|
||||
<label><select name="ground"><option>Noise</option><option>Time-Varying</option><option>Flat</option><option>Triangles</option><option>Catenary</option></select> Ground</label><br>
|
||||
<label><select name="controls"><option>Absolute Orientation</option><option>Relative Orientation</option></select> Controls</label><br>
|
||||
<label><select name="restitution"><option>1</option><option>< 1</option><option>> 1</option></select> e</label><br>
|
||||
<label><select name="gravity"><option>Normal</option><option>High</option><option>Off</option></select> Gravity</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script>
|
||||
var settings = {}
|
||||
for (const input of document.querySelectorAll("#controls input, #controls select")) {
|
||||
const read = () => { settings[input.getAttribute("name")] = input.getAttribute("type") === "checkbox" ? input.checked : input.value }
|
||||
read()
|
||||
input.addEventListener("input", read)
|
||||
}
|
||||
|
||||
var ctx = window.thing.getContext("2d")
|
||||
|
||||
const angleToVec = theta => [Math.cos(theta), Math.sin(theta)]
|
||||
const zipWith = (f, xs, ys) => xs.map((x, i) => f(x, ys[i]))
|
||||
const sum = xs => xs.reduce((a, y) => a + y, 0)
|
||||
const vecAdd = (a, b) => zipWith((x, y) => x + y, a, b)
|
||||
const hadamardProduct = (a, b) => zipWith((x, y) => x * y, a, b)
|
||||
const scalarMult = (a, n) => a.map(x => x * n)
|
||||
const dotProduct = (a, b) => sum(hadamardProduct(a, b))
|
||||
const vecLength = a => Math.sqrt(sum(a.map(x => x ** 2)))
|
||||
const normalize = a => scalarMult(a, 1/vecLength(a))
|
||||
function vsub(x, y) { return vecAdd(x, scalarMult(y, -1)) }
|
||||
function derivativeApproximation(f, a) {
|
||||
var delta = 0.000001
|
||||
return (f(a + delta) - f(a)) / delta
|
||||
}
|
||||
const rotate90CW = ([x, y]) => [y, -x]
|
||||
|
||||
var pixelDimensions = [window.thing.width, window.thing.height]
|
||||
var position = [0.5, 0.5]
|
||||
var velocity = [0, 0]
|
||||
|
||||
var keys = {}
|
||||
|
||||
window.onkeydown = ev => {
|
||||
keys[ev.key] = true
|
||||
}
|
||||
window.onkeyup = ev => {
|
||||
keys[ev.key] = false
|
||||
}
|
||||
|
||||
const toScreen = v => hadamardProduct(v, pixelDimensions)
|
||||
|
||||
function draw(start, end, color) {
|
||||
ctx.fillStyle = color
|
||||
var start = toScreen(start)
|
||||
var end = toScreen(end)
|
||||
ctx.fillRect(start[0], start[1], end[0] - start[0], end[1] - start[1])
|
||||
}
|
||||
|
||||
function drawLine(color, start, ...points) {
|
||||
ctx.lineWidth = 2
|
||||
ctx.strokeStyle = color
|
||||
ctx.beginPath()
|
||||
var s = toScreen(start)
|
||||
ctx.moveTo(s[0], s[1])
|
||||
for (const point of points) {
|
||||
var p = toScreen(point)
|
||||
ctx.lineTo(p[0], p[1])
|
||||
}
|
||||
ctx.stroke()
|
||||
}
|
||||
|
||||
var SPEED_OF_LIGHT = 0.01
|
||||
const gamma = v => (1 - (v/SPEED_OF_LIGHT)**2) ** (-0.5)
|
||||
const gammaDerivative = v => (v/(SPEED_OF_LIGHT**2))*((1-(v/SPEED_OF_LIGHT)**2)**(-1.5))
|
||||
|
||||
const noiseSeed = Math.random() * (2**32-1)
|
||||
const hash = (str, seed = 0) => {
|
||||
let h1 = 0xdeadbeef ^ seed, h2 = 0x41c6ce57 ^ seed
|
||||
for (let i = 0, ch; i < str.length; i++) {
|
||||
ch = str.charCodeAt(i)
|
||||
h1 = Math.imul(h1 ^ ch, 2654435761)
|
||||
h2 = Math.imul(h2 ^ ch, 1597334677)
|
||||
}
|
||||
h1 = Math.imul(h1 ^ (h1>>>16), 2246822507) ^ Math.imul(h2 ^ (h2>>>13), 3266489909)
|
||||
h2 = Math.imul(h2 ^ (h2>>>16), 2246822507) ^ Math.imul(h1 ^ (h1>>>13), 3266489909)
|
||||
return 4294967296 * (2097151 & h2) + (h1>>>0)
|
||||
}
|
||||
|
||||
const cartesianProduct = (xs, ys) => xs.flatMap(x => ys.map(y => [x, y]))
|
||||
const gradients = cartesianProduct([-1, -0.5, 0.5, 1], [-1, -0.5, 0.5, 1]).map(normalize)
|
||||
const gradientFor = (x, y) => gradients[hash(x+"."+y, noiseSeed) % gradients.length]
|
||||
|
||||
const interpolate = (a0, a1, w) => (a1 - a0) * (3.0 - w * 2.0) * w * w + a0
|
||||
const perlin = (x, y) => {
|
||||
const i = Math.floor(x), j = Math.floor(y)
|
||||
const u = x - i, v = y - j
|
||||
const n00 = dotProduct(gradientFor(i, j), [u, v])
|
||||
const n01 = dotProduct(gradientFor(i + 1, j), [u - 1, v])
|
||||
const n10 = dotProduct(gradientFor(i, j + 1), [u, v - 1])
|
||||
const n11 = dotProduct(gradientFor(i + 1, j + 1), [u - 1, v - 1])
|
||||
return interpolate(interpolate(n00, n01, u), interpolate(n10, n11, u), v)
|
||||
}
|
||||
|
||||
const GROUND_FUNCTIONS = {
|
||||
"Time-Varying": x => perlin(x * 10, x * 10 + Date.now() / 10000) * 0.25 + 0.75,
|
||||
"Noise": x => perlin(x * 10, x * 10) * 0.25 + 0.75,
|
||||
"Flat": x => 0.75,
|
||||
"Triangles": x => 0.25*(Math.abs(10*x-Math.floor(10*x)-0.5)) + 0.75,
|
||||
"Catenary": x => 1.3-Math.cosh(x-0.5)*0.5
|
||||
}
|
||||
const E_COEFFICIENTS = {
|
||||
"1": 1,
|
||||
"< 1": 0.5,
|
||||
"> 1": 1.5
|
||||
}
|
||||
const GRAVITY = {
|
||||
"Normal": 0.0001,
|
||||
"High": 0.0005,
|
||||
"Off": 0
|
||||
}
|
||||
|
||||
var direction = -Math.PI/2
|
||||
|
||||
var lastTime
|
||||
var SIMITERS = 1
|
||||
function loop(timestamp) {
|
||||
if (lastTime) {
|
||||
var timestep = timestamp - lastTime
|
||||
var scaling = 0.5 * timestep / 16.666666666666666666666666666666666666666
|
||||
lastTime = timestamp
|
||||
} else {
|
||||
requestAnimationFrame(loop)
|
||||
lastTime = timestamp
|
||||
return
|
||||
}
|
||||
var groundFunc = GROUND_FUNCTIONS[settings.ground]
|
||||
var reldir = settings.controls === "Relative Orientation"
|
||||
const scale = x => scalarMult(x, scaling)
|
||||
draw([0, 0], [1, 1], "black")
|
||||
ctx.fillStyle = "gray"
|
||||
for (let i = 0; i < pixelDimensions[0]; i++) {
|
||||
var gameX = i / pixelDimensions[0]
|
||||
var gameH = groundFunc(gameX)
|
||||
var pixelH = gameH * pixelDimensions[1]
|
||||
ctx.fillRect(i, pixelH, 1, pixelDimensions[1] - pixelH)
|
||||
}
|
||||
drawLine("green", position, vecAdd(position, scalarMult(velocity, 20)))
|
||||
if (reldir) {
|
||||
drawLine("blue", position, vecAdd(position, scalarMult(angleToVec(direction), 0.05)))
|
||||
}
|
||||
draw(vsub(position, [-0.005, -0.005]), vsub(position, [0.005, 0.005]), "white")
|
||||
|
||||
scaling /= SIMITERS
|
||||
for (var i = 0; i < SIMITERS; i++) {
|
||||
position = vecAdd(scale(velocity), position)
|
||||
var force = [0, GRAVITY[settings.gravity]]
|
||||
if (keys["w"]) {
|
||||
if (reldir) {
|
||||
force = vecAdd(force, scalarMult(angleToVec(direction), 0.001))
|
||||
} else {
|
||||
force = vecAdd(force, [0, -0.001])
|
||||
}
|
||||
}
|
||||
if (keys["a"]) {
|
||||
if (reldir) {
|
||||
direction -= 0.05
|
||||
} else {
|
||||
force = vecAdd(force, [-0.0005, 0])
|
||||
}
|
||||
}
|
||||
if (keys["d"]) {
|
||||
if (reldir) {
|
||||
direction += 0.05
|
||||
} else {
|
||||
force = vecAdd(force, [0.0005, 0])
|
||||
}
|
||||
}
|
||||
if (keys["s"]) {
|
||||
if (reldir) {
|
||||
force = vecAdd(force, scalarMult(angleToVec(direction), -0.0005))
|
||||
} else {
|
||||
force = vecAdd(force, [0, 0.0005])
|
||||
}
|
||||
}
|
||||
var divisor = settings.mechanics === "Relativistic" ? gamma(vecLength(velocity)) ** 3 : 1 //gamma(vecLength(velocity)) + gammaDerivative(vecLength(velocity)) * vecLength(velocity)
|
||||
if (isNaN(divisor)) {
|
||||
console.log("luminal limit exceeded, resetting")
|
||||
divisor = 1
|
||||
velocity = [0, 0]
|
||||
}
|
||||
var velocityChange = scalarMult(scale(force), 1/divisor)
|
||||
//console.log(gamma(vecLength(velocity)), velocity, velocityChange)
|
||||
if (settings.mechanics === "Aristotlean") {
|
||||
velocity = scalarMult(velocityChange, 60)
|
||||
} else {
|
||||
velocity = vecAdd(velocity, velocityChange)
|
||||
}
|
||||
if (position[1] > 1) { position[1] = 0 }
|
||||
if (position[1] < 0) { position[1] = 1 }
|
||||
if (position[0] > 1) { position[0] = 0 }
|
||||
if (position[0] < 0) { position[0] = 1 }
|
||||
//console.log(GROUND(position[0]), position[0])
|
||||
if (position[1] > groundFunc(position[0])) {
|
||||
var groundVector = normalize([1, derivativeApproximation(groundFunc, position[0])])
|
||||
var normalVector = rotate90CW(groundVector)
|
||||
velocity = vecAdd(scalarMult(groundVector, dotProduct(velocity, groundVector)),
|
||||
scalarMult(normalVector, -E_COEFFICIENTS[settings.restitution] * dotProduct(velocity, normalVector)))
|
||||
position[1] = groundFunc(position[0])
|
||||
}
|
||||
}
|
||||
requestAnimationFrame(loop)
|
||||
}
|
||||
|
||||
requestAnimationFrame(loop)
|
||||
</script>
|
@ -23,7 +23,7 @@ description: <a href="https://github.com/osmarks/guihacker">My fork</a> of GUIHa
|
||||
top: 0;
|
||||
left: 0;
|
||||
}
|
||||
.bars-and-stuff{
|
||||
.bars-and-stuff {
|
||||
left: 66.6%;
|
||||
}
|
||||
|
||||
@ -31,7 +31,9 @@ description: <a href="https://github.com/osmarks/guihacker">My fork</a> of GUIHa
|
||||
position: fixed;
|
||||
overflow: hidden;
|
||||
}
|
||||
p{margin:0}
|
||||
p {
|
||||
margin:0
|
||||
}
|
||||
|
||||
nav {
|
||||
display: none;
|
||||
|
@ -1,5 +1,5 @@
|
||||
---
|
||||
title: Heavpoot's Game
|
||||
title: Heav's Game
|
||||
slug: heavscp
|
||||
description: It is pitch black (if you ignore all of the lighting). You are likely to be eaten by Heavpoot's terrible writing skills, and/or lacerated/shot/[REDACTED]. Vaguely inspired by the SCP Foundation.
|
||||
---
|
||||
|
75
experiments/osmarkscalculator/index.html
Normal file
@ -0,0 +1,75 @@
|
||||
---
|
||||
title: osmarkscalculator
|
||||
description: Unholy horrors moved from the depths of my projects directory to your browser. Theoretically, this is a calculator. Good luck using it.
|
||||
---
|
||||
<textarea id="program" style="width: 100%; resize: vertical" rows="5"></textarea>
|
||||
<pre id="output"></pre>
|
||||
<button id="go">Go</button>
|
||||
<button id="clear">Clear Context</button>
|
||||
<select id="examples">
|
||||
</select>
|
||||
<script>
|
||||
const examples = {
|
||||
"blank": "",
|
||||
"factorial": `Fac[n] = Fac[n-1]*n
|
||||
Fac[0] = 1
|
||||
Fac[17]
|
||||
`,
|
||||
"expand": "(a+b)*(c+d)*(e+f)*(g+h)",
|
||||
"expand2": "(a+b)^3*(b+c)-d",
|
||||
"fibonacci": `Fib[n] = Fib[n-1] + Fib[n-2]
|
||||
Fib[0] = 0
|
||||
Fib[1] = 1
|
||||
Fib[6]
|
||||
`,
|
||||
"predicate": `IsEven[x] = 0
|
||||
IsEven[x#Eq[Mod[x, 2], 0]] = 1
|
||||
IsEven[3] - IsEven[4]`,
|
||||
"derivative": `D[3*x^3 + 6*x, x]`,
|
||||
"simplify": `x^a/x^(a+1)`,
|
||||
"simplify2": "Negate[a+b] + b",
|
||||
"arith": `(12+55)^3-75+16/(2*2)+5+3*4`,
|
||||
"subst": "Subst[x=4, x+4+4+4+4]"
|
||||
}
|
||||
const examplesSelector = document.querySelector("#examples")
|
||||
const program = document.querySelector("#program")
|
||||
for (const name of Object.keys(examples)) {
|
||||
const opt = document.createElement("option")
|
||||
opt.value = name
|
||||
opt.appendChild(document.createTextNode(name))
|
||||
examplesSelector.appendChild(opt)
|
||||
}
|
||||
examplesSelector.addEventListener("change", () => {
|
||||
program.value = examples[examplesSelector.value]
|
||||
})
|
||||
var worker = new Worker("osmarkscalculator.js")
|
||||
const forceKill = () => {
|
||||
console.warn("Force-terminating worker.")
|
||||
worker.terminate()
|
||||
worker = new Worker("osmarkscalculator.js")
|
||||
}
|
||||
const write = data => {
|
||||
const out = document.querySelector("#output")
|
||||
while (out.firstChild) { out.removeChild(out.firstChild) }
|
||||
out.appendChild(document.createTextNode(data))
|
||||
}
|
||||
document.querySelector("#go").addEventListener("click", () => {
|
||||
console.log(program.value)
|
||||
write("Running...")
|
||||
worker.postMessage(["run", program.value])
|
||||
var timeout = setTimeout(() => {
|
||||
forceKill()
|
||||
write("Execution timeout")
|
||||
}, 5000)
|
||||
worker.onmessage = ev => {
|
||||
const [status, result, time] = ev.data
|
||||
if (status === "ok") {
|
||||
write(result + `\nin ${time}ms`)
|
||||
} else {
|
||||
write("Internal error: " + result + `\nin ${time}ms`)
|
||||
}
|
||||
clearInterval(timeout)
|
||||
}
|
||||
})
|
||||
document.querySelector("#clear").addEventListener("click", () => worker.postMessage(["deinit"]))
|
||||
</script>
|
32
experiments/osmarkscalculator/osmarkscalculator.js
Normal file
@ -0,0 +1,32 @@
|
||||
let wasm_bindgen;(function(){const __exports={};let wasm;__exports.init_context=function(){wasm.init_context();};__exports.load_defaults=function(){wasm.load_defaults();};let WASM_VECTOR_LEN=0;let cachedUint8Memory0=new Uint8Array();function getUint8Memory0(){if(cachedUint8Memory0.byteLength===0){cachedUint8Memory0=new Uint8Array(wasm.memory.buffer);}
|
||||
return cachedUint8Memory0;}
|
||||
const cachedTextEncoder=new TextEncoder('utf-8');const encodeString=(typeof cachedTextEncoder.encodeInto==='function'?function(arg,view){return cachedTextEncoder.encodeInto(arg,view);}:function(arg,view){const buf=cachedTextEncoder.encode(arg);view.set(buf);return{read:arg.length,written:buf.length};});function passStringToWasm0(arg,malloc,realloc){if(realloc===undefined){const buf=cachedTextEncoder.encode(arg);const ptr=malloc(buf.length);getUint8Memory0().subarray(ptr,ptr+buf.length).set(buf);WASM_VECTOR_LEN=buf.length;return ptr;}
|
||||
let len=arg.length;let ptr=malloc(len);const mem=getUint8Memory0();let offset=0;for(;offset<len;offset++){const code=arg.charCodeAt(offset);if(code>0x7F)break;mem[ptr+offset]=code;}
|
||||
if(offset!==len){if(offset!==0){arg=arg.slice(offset);}
|
||||
ptr=realloc(ptr,len,len=offset+arg.length*3);const view=getUint8Memory0().subarray(ptr+offset,ptr+len);const ret=encodeString(arg,view);offset+=ret.written;}
|
||||
WASM_VECTOR_LEN=offset;return ptr;}
|
||||
let cachedInt32Memory0=new Int32Array();function getInt32Memory0(){if(cachedInt32Memory0.byteLength===0){cachedInt32Memory0=new Int32Array(wasm.memory.buffer);}
|
||||
return cachedInt32Memory0;}
|
||||
const cachedTextDecoder=new TextDecoder('utf-8',{ignoreBOM:true,fatal:true});cachedTextDecoder.decode();function getStringFromWasm0(ptr,len){return cachedTextDecoder.decode(getUint8Memory0().subarray(ptr,ptr+len));}
|
||||
__exports.run_program=function(program){try{const retptr=wasm.__wbindgen_add_to_stack_pointer(-16);const ptr0=passStringToWasm0(program,wasm.__wbindgen_malloc,wasm.__wbindgen_realloc);const len0=WASM_VECTOR_LEN;wasm.run_program(retptr,ptr0,len0);var r0=getInt32Memory0()[retptr/4+0];var r1=getInt32Memory0()[retptr/4+1];return getStringFromWasm0(r0,r1);}finally{wasm.__wbindgen_add_to_stack_pointer(16);wasm.__wbindgen_free(r0,r1);}};__exports.deinit_context=function(){wasm.deinit_context();};async function load(module,imports){if(typeof Response==='function'&&module instanceof Response){if(typeof WebAssembly.instantiateStreaming==='function'){try{return await WebAssembly.instantiateStreaming(module,imports);}catch(e){if(module.headers.get('Content-Type')!='application/wasm'){console.warn("`WebAssembly.instantiateStreaming` failed because your server does not serve wasm with `application/wasm` MIME type. Falling back to `WebAssembly.instantiate` which is slower. Original error:\n",e);}else{throw e;}}}
|
||||
const bytes=await module.arrayBuffer();return await WebAssembly.instantiate(bytes,imports);}else{const instance=await WebAssembly.instantiate(module,imports);if(instance instanceof WebAssembly.Instance){return{instance,module};}else{return instance;}}}
|
||||
function getImports(){const imports={};imports.wbg={};return imports;}
|
||||
function initMemory(imports,maybe_memory){}
|
||||
function finalizeInit(instance,module){wasm=instance.exports;init.__wbindgen_wasm_module=module;cachedInt32Memory0=new Int32Array();cachedUint8Memory0=new Uint8Array();return wasm;}
|
||||
function initSync(module){const imports=getImports();initMemory(imports);if(!(module instanceof WebAssembly.Module)){module=new WebAssembly.Module(module);}
|
||||
const instance=new WebAssembly.Instance(module,imports);return finalizeInit(instance,module);}
|
||||
async function init(input){if(typeof input==='undefined'){let src;if(typeof document==='undefined'){src=location.href;}else{src=document.currentScript.src;}
|
||||
input=src.replace(/\.js$/,'_bg.wasm');}
|
||||
const imports=getImports();if(typeof input==='string'||(typeof Request==='function'&&input instanceof Request)||(typeof URL==='function'&&input instanceof URL)){input=fetch(input);}
|
||||
initMemory(imports);const{instance,module}=await load(await input,imports);return finalizeInit(instance,module);}
|
||||
wasm_bindgen=Object.assign(init,{initSync},__exports);})();let loaded=false
|
||||
onmessage=async ev=>{if(!loaded){await wasm_bindgen("./osmarkscalculator.wasm")
|
||||
loaded=true}
|
||||
var[fn,...args]=ev.data
|
||||
let init=false
|
||||
if(fn==="deinit"){wasm_bindgen.deinit_context()
|
||||
init=false}else if(fn==="run"){const start=performance.now()
|
||||
try{if(!init){wasm_bindgen.init_context()
|
||||
wasm_bindgen.load_defaults()
|
||||
init=true;}
|
||||
postMessage(["ok",wasm_bindgen.run_program(args[0]),performance.now()-start])}catch(e){postMessage(["error",e.toString(),performance.now()-start])}}}
|
BIN
experiments/osmarkscalculator/osmarkscalculator.wasm
Normal file
14
src/index.js
@ -133,7 +133,7 @@ const applyTemplate = async (template, input, getOutput, options = {}) => {
|
||||
return page.data
|
||||
}
|
||||
|
||||
const addColors = R.map(x => ({ ...x, bgcol: hashColor(x.title, 0.5, 0.85), bordercol: hashColor(x.title, 0.7, 0.6) }))
|
||||
const addColors = R.map(x => ({ ...x, bgcol: hashColor(x.title, 0.7, 0.85) }))
|
||||
|
||||
const processExperiments = async () => {
|
||||
const templates = globalData.templates
|
||||
@ -230,13 +230,21 @@ const genServiceWorker = async () => {
|
||||
}
|
||||
const copyAsset = subpath => fse.copy(path.join(assetsDir, subpath), path.join(outAssets, subpath))
|
||||
|
||||
const doImages = async () => {
|
||||
copyAsset("images")
|
||||
globalData.images = {}
|
||||
for (const image of await fse.readdir(path.join(assetsDir, "images"), { encoding: "utf-8" })) {
|
||||
globalData.images[image.split(".").slice(0, -1).join(".")] = "/assets/images/" + image
|
||||
}
|
||||
}
|
||||
|
||||
const tasks = {
|
||||
errorPages: { deps: ["pagedeps"], fn: processErrorPages },
|
||||
templates: { deps: [], fn: loadTemplates },
|
||||
pagedeps: { deps: ["templates", "css"] },
|
||||
css: { deps: [], fn: compileCSS },
|
||||
writeBuildID: { deps: [], fn: writeBuildID },
|
||||
index: { deps: ["openring", "pagedeps", "blog", "experiments"], fn: index },
|
||||
index: { deps: ["openring", "pagedeps", "blog", "experiments", "images"], fn: index },
|
||||
openring: { deps: [], fn: runOpenring },
|
||||
rss: { deps: ["blog"], fn: genRSS },
|
||||
blog: { deps: ["pagedeps"], fn: processBlog },
|
||||
@ -245,7 +253,7 @@ const tasks = {
|
||||
manifest: { deps: ["assetsDir"], fn: genManifest },
|
||||
minifyJS: { deps: ["assetsDir"], fn: minifyJSTask },
|
||||
serviceWorker: { deps: [], fn: genServiceWorker },
|
||||
images: { deps: ["assetsDir"], fn: () => copyAsset("images") },
|
||||
images: { deps: ["assetsDir"], fn: doImages },
|
||||
offlinePage: { deps: ["assetsDir", "pagedeps"], fn: () => applyTemplate(globalData.templates.experiment, path.join(assetsDir, "offline.html"), () => path.join(outAssets, "offline.html"), {}) },
|
||||
assets: { deps: ["manifest", "minifyJS", "serviceWorker", "images"] },
|
||||
main: { deps: ["writeBuildID", "index", "errorPages", "assets", "experiments", "blog", "rss"] }
|
||||
|
11
style.sass
@ -70,6 +70,12 @@ ul
|
||||
|
||||
.isso
|
||||
padding: 1em
|
||||
overflow-x: clip
|
||||
|
||||
button, select, input, textarea, .textarea
|
||||
border-radius: 0 !important
|
||||
border: 1px solid gray
|
||||
box-shadow: none !important
|
||||
|
||||
.achievements
|
||||
position: fixed
|
||||
@ -105,3 +111,8 @@ ul
|
||||
text-align: right
|
||||
font-size: 0.8rem
|
||||
color: #555
|
||||
|
||||
.imbox
|
||||
display: flex
|
||||
img
|
||||
padding-right: 1em
|
@ -7,7 +7,10 @@ block content
|
||||
Stuff I say, conveniently accessible on the internet.
|
||||
div.blog
|
||||
each post in posts
|
||||
div(style=`background: ${post.bgcol}; border: 1px solid ${post.bordercol}`)
|
||||
.imbox(style=`background: ${post.bgcol}`)
|
||||
if images.hasOwnProperty(post.slug)
|
||||
img(src=images[post.slug])
|
||||
div
|
||||
div
|
||||
a.title(href=`/${post.slug}/`)= post.title
|
||||
small= renderDate(post.updated)
|
||||
@ -18,7 +21,10 @@ block content
|
||||
Various random somewhat useless web projects I have put together over many years. Made with at least four different JS frameworks.
|
||||
div.experiments
|
||||
each experiment in experiments
|
||||
div(style=`background: ${experiment.bgcol}; border: 1px solid ${experiment.bordercol}`)
|
||||
.imbox(style=`background: ${experiment.bgcol}`)
|
||||
if images.hasOwnProperty(experiment.slug)
|
||||
img(src=images[experiment.slug])
|
||||
div
|
||||
div
|
||||
a.title(href=`/${experiment.slug}/`)= experiment.title
|
||||
span.description!= experiment.description
|
||||
|