Warn­ing! Your devicePixelRatio is not 1 and you have JavaScript dis­abled! (Or if this mes­sage is not in a red box, your brows­er doesn't even sup­port ba­sic CSS and you can prob­a­bly safe­ly ig­nore this warn­ing. You'll en­counter way more sub­op­ti­mal things in that case...) Since HTML is as brain­dead as it can get, pix­el graph­ics might look off with­out javashit workarounds. Try to get a non-HiDPI screen or en­able javashit for best re­sults.

2025 updates

Cre­at­ed: 1761771101 (2025-10-29T20:51:41Z), 2131 words, ~9 min­utes

Tags: ,

Af­ter the lat­est more ma­jor-ish up­date to the blog in 2023, I de­cid­ed to go down the rab­bit hole again. So some rant filled up­dates in­com­ing.

Video player#

Prob­a­bly the most no­tice­able up­date is the re­moval of the Plyr based play­er. In the pre­vi­ous ar­ti­cle I de­scribed how I mod­i­fied Plyr to re­move most of the garbage, but of course I nev­er touched it again, so the site was run­ning on an un­main­tained fork of Plyr for years. It's not the case it didn't do its job still, but it had its prob­lems, and hon­est­ly it was eas­i­er to write a new play­er than fix­ing it. I al­so hat­ed its mod­ern non­sense, be­ing un­sty­lable, fuck­ing an­noy­ing dis­ap­pear­ing play­back con­trols, plus I had some fu­ture ad­di­tions in mind which would re­quire more in depth mod­i­fi­ca­tions to Plyr. And for rea­sons un­re­lat­ed to this blog, I had the "plea­sure" of work­ing on some web projects, so I had a bit more ex­pe­ri­ence in web abom­i­na­tions than when I did the last up­date.

So what's changed? Win­dows 9x style for the play­er too! No more au­to dis­ap­pear­ing play­back con­trols when not in fullscreen! Al­so, they're at the top of the video, not at the bot­tom, just be­cause. The scroll­bars aren't Win­dows like ei­ther.

What changed un­der the hood? A lot. I've con­vert­ed the javashit code to type­script, so it means npm crap to com­pile the site. Which is less than ide­al, as nanoc has ze­ro sup­port for any JS bundlers, so it's a dis­gust­ing hack now (rollup ba­si­cal­ly writes the bun­dle in­to nanoc's con­tent di­rec­to­ry), but it works. More on nanoc lat­er. But this means I can al­so use JS de­pen­den­cies to avoid rein­vent­ing the wheel, so I de­cid­ed to use a grand to­tal of 1 (one) JS li­braries: VanJS. Think re­act, ex­cept a few gi­ga­bytes small­er. I've most­ly port­ed the gallery to VanJS—I say most­ly, be­cause while it's us­ing VanJS now, if I were to rewrite it from scratch, I'd sure­ly ar­chi­tect it dif­fer­ent­ly, The video play­er is writ­ten in a more re­ac­tive style.

Maybe one of the most an­noy­ing thing in Plyr was how could I spec­i­fy thumb­nails for video. See the rant in the pre­vi­ous post, ba­si­cal­ly I had to make a VTT file with a weird syn­tax (VTT is nor­mal­ly for sub­ti­tles, Plyr (ab)used it for thumb­nails), then make a tex­ture at­las how­ev­er I like. How­ev­er Plyr didn't sup­port chang­ing res­o­lu­tion of the thumb­nails, which was a prob­lem for videos with chang­ing res­o­lu­tions. I had to workaround it by let­ter­box­ing all thumb­nails to a com­mon res­o­lu­tion, but with the new play­er I no longer have to do it! Just change VTT to a more sane (and com­pact) for­mat is all I what I need­ed to do, but... There's al­ways a but. While try­ing to fig­ure out the in­ter­val used by YouTube to take snap­shots (I didn't find the an­swer), I ran in­to a ran­dom stack­over­flow ques­tion, quot­ing the rel­e­vant part of the an­swer:

Op­ti­mize or­der of down­loads. For ex­am­ple, if you have video with length 2:55:

  1. First, down­load con­tain­er im­age with 8 thumbs cov­er­ing full range of video time: 0:00, 0:25, 0:50, 1:15, 1:40, 2:05, 2:30, 2:55. This makes your ser­vice to ap­pear as "work­ing in­stant­ly" for slow clients.
  2. Next, down­load 4 con­tain­er im­ages with 32 thumbs to­tal cov­er­ing full range of video, but more dense­ly: 0:00, 0:06, 0:11, 0:17, ...
  3. Now, down­load grad­u­al­ly all oth­er thumb­nails with­out any par­tic­u­lar or­der.

Hmm... Let's make it a bit more reg­u­lar, and we have a work­ing so­lu­tion. I called a set of im­ages above a "lay­er", and for ex­am­ple with 4 lay­ers the so­lu­tion could look like:

And this can be made to work with any num­ber of lay­ers, with n lay­ers, lay­er l will con­tain thumb­nails where i % (1 << (n-l-1)) == 0. One down­side here is for every l >= 2, you'll have more thumb­nails than on the pre­vi­ous lay­er, so dif­fer­ent lay­ers have dif­fer­ent size. But on the oth­er hand, the im­por­tant lay­ers are small­er and thus are faster to down­load. Right now the longest videos on this blog have 4 lay­ers, short­er ones have less, but the im­por­tant thing is, even if you have a slow in­ter­net con­nec­tion, you should get a rough thumb­nail set pret­ty fast!

Oh and thumb­nails, video posters can be JXL and scaled too now. Pre­vi­ous­ly Plyr on­ly sup­port­ed spec­i­fy­ing a sin­gle im­age, but with the new play­er is no longer an is­sue. Now, cur­rent­ly JXL is prac­ti­cal­ly on­ly sup­port­ed by Pale Moon, so it's prob­a­bly not too use­ful, but bet­ter HiDPI sup­port is wel­come.

One change which might af­fect non-javashit users a bit neg­a­tive­ly is that you'll no longer have a HTML video em­bed. The prob­lem is, while in prin­ci­ple a sin­gle <video> el­e­ment can have mul­ti­ple <source> el­e­ments, I don't know of any brows­er which al­lows you to se­lect which one to use. They're on­ly good for list­ing mul­ti­ple for­mats where the brows­er on­ly sup­ports one of the for­mats, not when you want it user se­lec­table. And hav­ing mul­ti­ple in­stances of the same video next to each oth­er in dif­fer­ent qual­i­ty would just look stu­pid, so non-JS users will see links for the dif­fer­ent qual­i­ty video files (sim­i­lar to what they al­ready have with the gallery).

Al­so fun fact: while re­do­ing the thumb­nails, I no­ticed I didn't in­clude a video about track pre­sen­ta­tion (or lack of it) in the NFS5 post. I made a video, I put it in­to git, due to how nanoc works, it was even de­ployed on the serv­er, just nev­er ref­er­enced any­where. The best of all, in the NFS6 post I even wrote pre­sen­ta­tions are back, de­spite fail­ing to even men­tion it in NFS5's ar­ti­cle... So you might want to go back and check the new rant­i­ng I added there for your en­joy­ment, and sor­ry for for­get­ting about it the first time.

HiDPI support#

Back when I first checked how the blog looks un­der a HiDPI screen, I quick­ly added a big warn­ing on top of the page:

Warn­ing! Your devicePixelRatio is not 1! (Are you a phonefag or us­ing a meme 4k mon­i­tor?) This site might ap­pear dis­tort­ed. Re­set your brows­er's zoom lev­el (or use Zoom text on­ly in Pale Moon/Fire­fox). If you use Pale Moon/Fire­fox, set layout.css.devPixelsPerPx to 1 in about:config. If you use a chromi­um based brows­er, launch it with --force-device-scale-factor=1. Note that this will prob­a­bly make every­thing un­read­able, but at least it won't fuck up pix­el graph­ics. Don't buy a HiDPI garbage next time.

Then as time went on, I im­proved HiDPI sup­port. Part of it was the fact I couldn't buy a new lap­top with a non-HiDPI screen with oth­er­wise ac­cept­able specs... Part of it is the new video play­er, where I could fix things I couldn't with Plyr pre­vi­ous­ly. Now pix­el thumb­nails/videos (and gallery/video UI el­e­ments) are sup­posed to be on­ly scaled by an in­te­ger fac­tor, every­thing else should be scaled more-or-less nor­mal­ly. There's a small prob­lem with UI, though, re­cent­ly CSS zoom be­came stan­dard. When I start­ed work­ing on the HiDPI sup­port, on­ly Chromi­um and Sa­fari sup­port­ed it as a non-stan­dard ex­ten­sion orig­i­nat­ing from In­ter­net Ex­plor­er, but now al­so Fire­fox sup­ports it, so I got my hopes up maybe I can re­move all the workarounds... Ex­cept Pale Moon still doesn't sup­port it. So now on mod­ern browsers you get CSS zoom, on Pale Moon you get a fil­ter based workaround, which is not per­fect. Hope­ful­ly Pale Moon will add sup­port for zoom soon as it's stan­dard now, and I can re­move this dis­gust­ing workaround, as it com­pli­cates non Pale Moon code too, but we'll see. It al­so lacks sup­port for CSS min()/max() func­tions...

Getting ready: nanoc?#

This blog is cur­rent­ly be­ing built with nanoc. And while in gen­er­al I like it, there are some prob­lems with it, which makes us­ing it in­creas­ing­ly an­noy­ing.

First is the text and bi­na­ry item han­dling. Every file is ei­ther text or bi­na­ry, on­ly spec­i­fied by their ex­ten­sion (so you can't do pat­tern match­ing on it), and they work com­plete­ly dif­fer­ent­ly (text files are read in­to mem­o­ry by nanoc, while bi­na­ry files are passed around as file­names). The thumb­nail gen­er­a­tor for the videos out­puts a YAML file with in­fo about the thumbs, YAML is a tex­tu­al for­mat, so it should be a text file, right? No! In nanoc, you can put meta­da­ta at the be­gin­ning of a file, for ex­am­ple to give the page a ti­tle or some­thing, be­tween a pair of --- head­ers. This is fine, ex­cept YAML files gen­er­at­ed by Ru­by al­so start with ---. And if you mark some­thing as text file, nanoc will try to parse these blocks no mat­ter what, and bail out say­ing your YAML file is in­valid since it can't find a sec­ond --- line. Nanoc's doc­u­men­ta­tion even has a note about this er­ror (of course the an­chor won't go to the er­ror, be­cause hav­ing a free Pales­tine ban­ner in a fuck­ing soft­ware doc­u­men­ta­tion is more im­por­tant than hav­ing a doc­u­men­ta­tion for the fuck­ing soft­ware that fuck­ing ac­tu­al­ly fuck­ing works for any­thing oth­er than fuck­ing virtue sig­nal­ing), and the so­lu­tion is to add two more --- to the head­er. Yeah, I'm go­ing to fuck up all the YAML files so just this id­iot tool won't shit it­self. In the end I went with the al­ter­na­tive of mark­ing these YAML files as bi­na­ry, be­cause ap­par­ent­ly in nanoc, text files can't start with ---.

The oth­er prob­lem with bi­na­ry items is snap­shots. Nanoc by de­fault cre­ates three snap­shots for each item, which while can be use­ful at times, in my ex­pe­ri­ence 99% of the time com­plete­ly un­nec­es­sary. And since bi­na­ry items live in files, this in­volves mak­ing at least 4 copies of each bi­na­ry file (the 3 snap­shot, plus the file in the out­put dir). Of course this is not a big deal if you have a few kbyte sized files, but with video files hav­ing a size of a cou­ple hun­dred megabytes, these copies add up quick­ly. I've worked around this by mon­key patch­ing nanoc so it just sym­links the files in­stead of copy­ing. (New­er nanoc ver­sions are sup­port­ed to use re­flink copies on BTRFS, so it won't ac­tu­al­ly oc­cu­py disk space more than once, but it still blows up du out­put and gives a lot of ex­tra work to file back­up/sync­ing tools.)

The above is just an­noy­ing, but here comes the deal break­er: nanoc is ridicu­lous­ly slow. To have nanoc just print the help mes­sage takes about 300 ms. I guess nanoc should be re­named to bloatc to slowc. A no-op re­build (when noth­ing has to be re­built, and every­thing is cached in­to mem­o­ry) takes about 4 sec­onds. A change in lib/ (which trig­gers a full re­build), but with­out any change in the out­put, is about 33 sec­onds. Oh and this is with ruby --jit, with­out JIT it's 4.4 s and 36 s (well, JIT doesn't help much). I didn't dare test­ing what hap­pens if I delete the out­put di­rec­to­ry, it would prob­a­bly take min­utes. On a tiny blog with 19 posts. What would hap­pen if I had 1000 posts? Would it take a half hour to com­pile it?! With some hacks I man­aged to de­crease the lib touch re­build time to 12.5 s, but I don't know what to do about the no-op re­build. Nanoc's de­pen­den­cy track­er is just that slow.

Oh and just a quick tip. Don't write @items['foo']. While it looks nice, if it can't find an item named foo ex­act­ly, it will switch in­to glob­bing mode, and try to fnmatch EVERY FUCKING ITEM against your string which con­tains ze­ro wild­card char­ac­ters! So it's sud­den­ly O(n) with a big­ger con­stant in­stead of O(1). Nanoc gives you free hash flood­ing in a project where there is no un­trust­ed in­put! The cor­rect an­swer is the mouth­ful @items[Nanoc::Core::Identifier.new id]. Yeah, nanoc is al­ready thrash­ing the GC, so this will help it for sure, but at least the al­go­rithm stays O(1).

What would I change from nanoc to, I don't know. It's not like nanoc's doc­u­men­ta­tion has a "ridicu­lous­ly slow" point un­der fea­tures, and with small toy sites I can't re­al­ly test the speed of al­ter­nate sta­t­ic site builder tools. Of course, there's al­ways the al­ter­na­tive of rolling my own tool, de­signed to be par­al­lel from the get go, but I don't want to do it yet. But I didn't want to write my video play­er ei­ther...