74 karma · joined January 21, 2024
- mp4tag.io (idk if still works) - hn.zip (unfinished) - loopbox.app (unfinished)
- I wrote my own scraper for HN for reasons I no longer remember
- Scraper is called every 30 seconds by an Apps Script timer on a Google Sheet (to be dirt cheap, Vercel doesn't have free cron jobs).
- Caches the front page, plus ten comments per post into a single Firestore document (to save money on reads vs 20 reads per load)
- Home page loads via SSR with Nuxt.
- Post pages will load the rest of the comments from a separate doc for each post
- the other comments load instantly on navigation if on a halfway decent connection - but if not, you still get to see the first 10 comments to get an idea of the discussion while you wait.
- Added some Workbox stuff to get it offline-first.
- Added an IndexDB wrapper around Firestore for more control over cache behavior I think.
- It's worked now for over a year with no oversight needed and not costing a penny!
Then I got distracted because I wanted to rebuild it from scratch to not use Nuxt and never completed that rewrite. Been a while since I've worked on it, had no idea other people even knew it existed! There's definitely some bugs (I think CSS gets broken on deeply nested comments) but it worked enough for my uses so I moved on.
#1 - Function Wrapper:
(r = n => setInterval(t => {
...
}, 100))()
Here a number of things are done:- set `r` to the function that runs the whole quine.
- when r is called (inline immediately at the end) it sets an interval to call the body every 100ms.
- `n` is unused, and is there for spacing instead of using `()` to indicate no params.
#2 - Row Looper:
for (j = o = "\n", y = 5; y--; document.body['innerHTML'] = "<pre><" + (S = "script>\n") + o + "\n\n</" + S)
- create a loop that will occur 5 times (one for each row of the output).- initialize some variables `j` and `o` to newlines. `o` will contain our rendered output, `j` will soon become an incrementor.
- after each loop, put the contents of 'o' between two strings of "<script>".
- the `S = "script>\n"` portion helps with spacing and S is no longer needed after this line.
#3 - Column Looper:
for (x = -001; x++ < 63; o += `(r=${r})()`[j++].fontcolor(c ? '#FF0' : "#444"))
- loop through the 64 columns, incrementing x and j.- x keeps track of the column, j keeps track of the character within the Function `r`.
- each loop, `o` adds a letter from `r`. (In Javascript, functions can be converted to strings which contain their source).
- Also add the `)()` to the end of `r`, which the implicit Function.toString() will not have.
- Set the fontcolor on that string based on `c` - String.fontcolor() is an old deprecated method which wraps your string in a `<font>` tag.
#4 - Renderer:
c = x / 2 % 4 < 3 && [31599, 19812, 14479, 31207, 23524, 29411, 29679, 30866, 31727, 31719, 1040][(D = Date()[16 + (x / 8 | 0)]) < 10 ? D : 10] & 1 << (x / 2 | 0) % 4 + 3 * y
- The array of numbers is essentially a font, defining the numbers 0..9 and lastly ":"- We pick which character of this font to render based on a Substring of Date(). Either a number, or ":".
- Date()[16] is where the Time string starts, and chars are rendered 8 blocks wide.
- With the beginning `x / 2 % 4 < 3` we render 2 spaces of dark characters between numbers.
- At the end, render our `font` with the x and y coords
- x is divided by two, so all pixels in this font are two characters wide.
- font glyphs are 3x5, and thus defined as 15 bits.
- for example, the glyph for '0' is:
111
101
101
101
111
- which results in 0b111101101101111 and therefor 31599
- To render these characters, we bit shift (<<) the number by the row & col*width and see what value is in the `1` place.
#5 - Coming togetherNow just travel the last few steps back up the chain again, and you can see how these characters are placed in `o` - and if `c` is true (we hit a character) it is rendered yellow. `o` is put between a "<script>" and that resulting string is put in document.innerHTML every 100 milliseconds.
So yes, use distilled water... but in terms of risk factor, I don't think it's too high.
As for how the site works - make sure you have a microphone and ideally headphones. Click START and you'll hear a metronome. Click ADD TRACK to record a loop. If you're musically inclined you can combine multiple recordings to make a short looping song, by either adding bars to a loop or layering multiple tracks. Hit CTRL or right-click (long press on mobile) a track to modify how it sounds or delete it.
I'll post it to Show HN hopefully soon.