Andrew's Blfog

On sunsetting projects

Recently, tktech over at tkte.ch decided to discontinue running two services he's provided for free for over a decade each: Notifico and wiki.vg. As of writing, this will take place on Nov 30th, 2024.

The former isn't as interesting to me anymore, since the world has moved on to chat platforms that have first-class webhook support and a middle-man to dispatch notifications isn't as necessary anymore, but the sunsetting of wiki.vg is of interest to me, since most of ClassiCube's developer userbase uses the protocol documentation and the .

read more

Discord pop-up ad jumpscare

discord has ads now and every time I see something that reminds me of them I have conniptions

[Image: thanks discord!!]

typical vc-funded slop. you'll never ever see this on pi.andrewph.org

editor's note: I couldn't get a real ad to pop up for demonstration purposes, because I use Discord in a browser and block the ads.

read more

Just recently thought I'd try out Bluesky due to whatever recent Twitter drama is going on (I don't seriously use any microblogging platform beyond messing around) and I found it quite nice, so I've recorded my initial thoughts here for SEO (and also to test the new blog updates).

Additionally, I've stuck a link to my Bluesky profile in the header for the time being.

Twitter

The OG platform-turned-garbage-heap. It was OK back in the day and its main value was introducing people to the concept of "microblogging", made more fun by the fact that, at the time, you only had 140 characters to work with per message, encouraging clever word usage.

Nowadays, the platform has been enshittified - you must have an account to see anything other than one message at a time, and it aggressively suggests you should register on every page you're on. You can pay for a subscription that does... something. Might give you a little kickback for reach/interaction? Unsure, but it's a microblogging website. Why is that even necessary?

Your "handle" (or, the "username" as some people call it, the part after the @) is globally unique, and is how users refer to you. It's formatted like . The opportunity for vanity handles is slim, but there are some nice examples out there.

read more

A while back, Reddit made some controversial changes to their API pricing scheme (going from "free" to "outrageously expensive and clearly meant to just kill competition") resulting in almost all third-party Reddit browsers to have to close up shop. This included Apollo, my personal favorite.

The fallout from this change blew fresh wind into the sails of the maintainers of Lemmy, and brought a surge of users to all federated platforms. (Elon's acquisition of Twitter and subsequent rename to X also had this same effect, but for Mastodon and other micro-blog software). The promise of a decentralized and federated system that otherwise feels the same as the walled garden of Reddit is tantalizing, after all.

"decentralization" and "federation"

I'm not going to waste much time on explaining these - I'm definitely not the right person to explain it correctly so take these definitions with a grain of salt.

Decentralization: where a service isn't hosted by one person or corporation, but instead hosted by many. Torrents, for example, are decentralized.

read more

Nginx vs Caddy

I recently migrated my blog from a small server hosted by a friend to a large server with 2TB of space. This was a technical necessity, I assure you.

While doing so, I decided to try migrating everything I ran to use Caddy, a reasonably performant nginx-like webserver written in Go. What do I mean by nginx-like? It's fast and sometimes very cryptic.

I use Caddy on my home network to proxy internal services to be world-accessible. It works pretty well for that! Turns out, probably 99% of what I use nginx for can also be done with Caddy.

Roadblocks

I extensively use Cloudflare as a reverse-proxy and CDN for my services. This is great, but has the downside of obscuring your visitor's IPs unless you take special care to correct it. Cloudflare provides the canonical client IP in the Cf-Connecting-Ip header, and also provides a list of ipv4 and ipv6 addresses that you can trust (their endpoints) - which is fantastic.

read more

Javascript generator functions

It has been a while since my last blog post!

I learned recently about generator functions, having successfully avoided them for over a decade. The basic idea in Javascript is that they are a function that returns a special iterable object. Iterable in this case meaning you can loop over it.


function* generatorFunctionOne () {
    yield "a";
    yield "b";
    yield "c";
    yield "d";
}

const g = generatorFunctionOne();

for (const yieldedItem of g) {
    console.log(yieldedItem);
}

/* 
 * Logs, in order:
 * - a
 * - b
 * - c
 * - d
*/

That's a rather contrived example, but more-or-less shows the base concept - each next call (or each iteration) will continue up to the next yield, and whatever that yield is will be provided as the iterable element, and can be repeated ad nauseum via a loop. So, for example, if you wanted to generate n random numbers...

read more

A cool trick I learned when starting work on my blog is that you can do tooltips entirely with CSS and attributes inside your HTML.

For the uninitiated, when I write "tooltip" I mean the little black dialog box that provides additional information about something on hover. For example, the "discord" and "twitter" buttons on this website use tooltips.

[Image: A "tooltip" in action.]

Typically, these are done in Javascript. However, thanks to the CSS attr() function and wide support for psuedo-elements (::before and specifically) you can fairly trivially do it all with clever CSS.

read more

Photoshop in 2021/2022

Adobe not my main ho. That means this shit wasn't sponsored by them. Thanks.

The cost of Photoshop has long been a huge meme, and piracy of the Adobe suite is (and always has been) rampant. After all, do you really expect that 15 year old making graphics for his Minecraft server to have ponied up $700 for the latest Photoshop?

Then along came Creative Cloud. Who would want to pay monthly for Photoshop, when they could pay once and use that version forever?

As with any software, you lose out on new features if you don't update. And you can't update if you don't pay more. With fairly frequent major releases, keeping up-to-date with Photoshop (and other Adobe suite applications) quickly becomes a costly endeavor.

But remember the question I posed 2.5 sentences ago? Who would want to pay monthly for Photoshop? Well, it turns out (as of the time of writing) you can pay $10 per month and get the latest version of Photoshop...

read more

This is some wacky stuff, but I needed it for this blog so I didn't have to work super duper hard to reduce the front page size.

Basically, I have images in blog posts, and removing them so you don't have to load 30MB of images when you go to the main page is imperative. I was accomplishing this using an ugly regex, but it was buggy and didn't let me to arbitrary processing on each image node.

So I did what any sane person does, and wrote a processing function to traverse the post DOM. I author all of my posts in markdown and the upside is that it's easy to read the source, and it gets turned into super clean HTML. The blog software does the heavy lifting of normalizing each post into pure HTML for me, and I can run code on the output.

For this, I used the PHP DOMDocument stuff which is horrible, don't get me wrong, but it's significantly better than any other path I found.

read more

CSS mask-image Property

I was doing some hacky stuff with a psuedo-element to make posts "fade" into the background, but that had the downside of requiring a solid background color to fade it into, and didn't let you highlight text on the post body when faded.

With the mask-image property, I can cleanly fade out posts on the front page with whatever kind of background I want. To demonstrate, I made the background kinda wacky.

The downside here is that it doesn't work on IE, but since I use CSS vars anyway, I don't really care. IE is dead.

read more

I got an email from Steam telling me that ClassiCube is now Steam Deck Verified.

[Image: nice]

That means a human at Valve played ClassiCube on a Steam Deck and checked all the boxes.

I find this interesting because the game doesn't have controller support or anything, but I guess the out-of-box Mouse/Keyboard Steam Input bindings did the trick.

read more

Version control is a rare tool that I would say is absolutely required, even if you are only using it as a solo developer. Some people say that it's a tool that you live and die by, I agree with that assertion.

~ Robert Venables, September 2009

Version Control is a dummy useful class of software that has helped me out countless times. Atlassian has a pretty good overview of what it is and how it generally works.

For the uninitiated, it's basically where you use a software to semi-manually keep track of changes you make to code, text, or anything really. It will see that a file has changed, you type in a comment to help you remember what you changed, then you save it to a "repository" where you can recall every single change you've recorded. Usually, this repository will be hosted on a server, like or or whatever, but it's not a hard requirement.

read more

Finding Large Files (on Linux)

If you've ever needed to figure out what files are taking up all the space on your Linux system, you can use this snippet to do so!

sudo find / -type f -printf "%s\t%p\n" 2>/dev/null | sort -nr | head -n 20

This lists the 20 largest files on your system. You can increase the 20 at the end to increase the amount of results.

For a (probably) more useful view, you'd probably want to use something akin to WinDirStat. My utility of choice is WizTree which is SUPER fast because it will scan the file table instead of asking the OS to tell it what's in a directory and recursing. The results are clear, though you do miss out on this extreme speed when using it on a non-NTFS drive.

read more

Neat New Tags

I added a nifty little style to the tags on every post. Now they're all cool looking.

I did this with a little bit of CSS wizardry. Here's how you can achieve the same results!

The theory of this trick is to use a psuedo-element, fit it to the tag element's size, style the psuedo-element instead of the tag itself, and drop it behind the tag.

Assuming the tags have a class of "articleTag"...

.articleTag {
  position:relative;
  color:#000;
}

.articleTag::before {
  content: '';
  position:absolute;
  background:#f00;
  z-index:-1;
  top:0;
  right:0;
  bottom:0;
  left:0;
  transform:skew(25deg);
}
read more

NPM sucks

I always find frontend devs way of thinking to optimization interesting. Shaving few KBs of JavaScript but load dozens of MBs images. Then talking about avoiding dependencies but using node packages that indirectly pull in hundreds of other dependencies. Loading scripts from dozens of HTTP connections, etc.

~ some guy on the internet, July 2020

having to wrangle npm is a nightmare of dependencies that is never-ending with some packages.

it's ridiculously easy to upload npm packages, which leads to people creating dependencies for things like checking if a number is odd. Compartmentalizing things into reusable bits isn't really all that bad, but for some reason npm takes it to the extreme.

read more

You Don't Need 45KB of Tracking Code

Web Analytics are helpful, but not helpful enough for me to warrant quadrupling my blog's bandwidth costs.

I like having a general idea of how people are interacting with my sites, and how many people see them. Until I made this blog (and had a slight focus on low page size), I didn't really care to check how bulky Google Analytics was. Now I know.

Adding GA to a website will cost you around 45KB of network bandwidth if you use the recommended Google Tag Manager way.

On the extreme other end of the spectrum, Plausible Analytics weighs in at under 1KB, and offers a lot (most?) of the same information, with slight stat skewing; allegedly Plausible reports slightly higher (15%-ish) unique viewers, probably because it's a little more privacy-oriented and not nearly as aggressive in its session/user tracking.

Plasubile sounds great, but I'm garbage at using Docker and didn't want to pay for the SaaS version, so I installed Umami, which is almost as lightweight (I think the analytics script ends up being around 2KB) and can use MySQL. The rest of those analytics softwares seem to use Postgres, which I don't have installed and configured (and didn't feel like doing so)

read more

Back up your shit!

It's really important to back your files up. Most people don't think about it that much, but it really matters. On top of that, backups must be partially recoverable. A backup is of no use if you have to load the entire (possibly massive) backup onto existing hardware just to pull one file out.

It's almost kicked me in the butt several times that I hadn't backed up my data reliably. ClassiCube probably wouldn't still be around (at least not in the same capacity as it is now) if I didn't get lucky with a backup made shortly before a huge unrecoverable disk failure.

At home, I have a NAS with a nice chunky hard drive that runs a python script every night at 11pm, which rsync's data from all of my linux servers on the internet. It's really helpful for me, so maybe you'll find it useful too:

import os,sys
os.chdir(os.path.dirname(sys.argv[0]))

DEBUG_RUN = False

BACKUP = {
    "example_name": {
        "host": "root@192.168.0.44", # must be reachable with current server's ssh key
        "source": [
            "/etc/nginx", # nginx configs
            "/etc/mysql", # mariadb configs
            "/var/lib/mysql", # mysql data (won't be perfect but should be recoverable)
            "/home" # home directory (why not?)
        ]
    },
}

def main():
    for server in BACKUP:
        s = BACKUP[server]
        save_dir = server
        args = ['rsync', '-trvz', '--links']
        if DEBUG_RUN:
            args.append("--dry-run")
        if type(s['source']) is str:
            sources = [s['source']]
        else:
            sources = s['source']

        for source in sources:
            if source.startswith('/'):
                source = source[1:]

            levels = source.split('/')

            if levels > 1:
                curlvl = ''
                for level in levels[:-1]:
                    curlvl = "{}{}".format(curlvl,level)
                    args.append('--include="{}"'.format(curlvl))
                    curlvl = "{}/".format(curlvl)

            if not source.endswith('/'):
                source = '{}/'.format(source)

            source = '{}***'.format(source)
            args.append('--include="{}"'.format(source))

        args.append('--exclude="***"')
        args.append('-e')
        args.append('"ssh"')
        args.append("{}:/".format(s['host']))
        args.append("share/backups/{}".format(server))

        run(args)

    run(['chmod', '777', 'share/backups/', '-R']) # clobber the permissions so I can view them on smb

def run(args):
    print(" ".join(args))
    su = os.system(" ".join(args))

if __name__ == '__main__':
    main()

Thanks to this, I rest easy knowing that my data is at least somewhat safe

what if I have a housefire or the drives in my NAS die?

I haven't gotten that far ahead yet - recurring payment solutions for more than random bits haven't really been in my budget until the last year, so I've just barely scratched the surface of the research I need to do.

read more

Discord Sucks for Information Sharing

Long ago, people used these things called "forums" (or "bulletin boards") to speak to other people online, in a way that was publicly accessible and crawlable in most cases.

That was great, it was the golden age of information sharing. It allowed search engines such as Google to not only rise to glory, but also to help people find the information they need. So much information was just out in the open for search engines to crawl, and then subsequently viewed without the need for a third-party application.

This also meant that searching that information could be performed by more than just the search functionality built-in; Say there's a certain search engine that is able to search Russian language in a more natural way, but the search function built-into the site is more oriented towards the English grammatical structure- no problem! just use that other search engine.

Now we have Discord, a chat application being used in place of forums almost entirely in many cases. Sure, it's convenient and the features between "servers" (really more akin to guilds, as they're called in the backend) are consistent-ish, but it's got a lot of serious problems.

ok boomer

What problems does Discord have, exactly?

read more

big stupid test

I made a discord notification plugin for this thing and this is a post designed to test if it works.

what do you mean

cheese isn't

a food group

anymore

read more

The Fastest Website in the West

Have you ever wanted to make a super fast website, that can download and execute faster than a neuron in your brain can fire? Have you ever wanted to brag to your friends about how your website, on a fresh load, only downloads 12KB of data?

You're in luck. Here are my top 3 ways to not make a bloated crap website.

Be willing to be inflexible; skip the frameworks (if you can)

It sure is nice to have bootstrap included for everything, but unless you're making something complex, it's really not worth including at all. In this case, I've made a customized bootstrap grid + reboot css file that only contains what I think I'll need, and has everything I'll never use stripped out completely.

Frameworks are a massive help, especially when you're newer to the web. If you don't have a clear vision for what you're designing, or want the flexibility a framework gives you, there's no harm in doing what makes sense.

That's not the point of this post though.

Don't use big ol' javascript libraries

read more

enter: bludit

In the ever-increasing urge to become a blogging mother, finding a blog software that does what you want while not being too bloated can be an insurmountable task.

For example, if you want to use Wordpress, you have to choose between hosting it yourself (much more control over it, but requires you to have some knowledge of installing web software and requires that you have hosting already) or using wordpress's managed hosting @ https://wordpress.com/ .

Then, once you have figured out how to get a blog set up, you have to deal with the fact that there's only a handful of good themes, and that your blog will look the same as everybody else's that uses that theme, and that it's pretty difficult to modify the themes in any meaningful way.

To make matters worse, theming is much harder in Wordpress because of how complex and old it is. Sure, it's got pretty good documentation - but who wants to spend 10-20 extra hours implementing a design into a wordpress theme?

The second best alternative seems to be static site generators - they are usually super simple to theme, but they have the limitation that you can't usually author their content on the web in an accessible way, and there's absolutely no true dynamic content you can put on it (eg a proper search, though you should probably defer to google anyway for that).

read more