‹ go back

Hanky-Janky.

Published 03 March 2024 at Yours, Kewbish. 2,269 words. Subscribe via RSS.


Introduction

Julia Evans wrote a blog a while ago about writing tiny personal programs — the kind of thing where a website or service has some friction in behaving as you want it to, and you bust out some Python or Bash to wrestle it into compliance. Because the original site wasn’t working as intended, these scripts tend to use features not intended for general consumption, since if the features were integrated into the main software you’d probably not be writing the script in the first place. Some examples include manually inspecting web requests to hit the same internal API endpoint, or scraping HTML and parsing it with BeautifulSoup. As well, these scripts are usually written for a single user or maybe a small group of users, so it’s reasonable to slap it together as fast as possible without much regard for its scalability or maintainability. If you only have a couple users, it’s easier just to give them a detailed list of instructions and constraints (‘don’t press the big red button until you’ve typed into this text field!’) than worry about handling edge conditions gracefully. When you see someone write a script like this, what words come to mind?

Two that spring to mind for me are hacky and janky: they’re hacky, because they work around the limitations of what’s presented, usually in an unintended way; and they’re janky, because they’re delicately cobbled together and fragile. I also find that hacky and janky both tend to be synonyms of mischief, and this sort of well-intentioned mischief and app misuse is what I’d like to touch on today.

This janky mischief has a spectrum of seriousness. On one hand, you have Evans’ scripts, which mostly serve some real-world problems, like finding vaccine appointments. On the other, you have satirical (sorry, I meant very deeply serious) conferences like SIGBOVIK and humorous stories on HN that just hack for the sake of it. The stories I’ll share in this post lie somewhere in between — my workarounds all do serve a genuine purpose, but I also find the way I went about implementing them very comedic1.

This is a post about my recent hacky hanky-panky and a hacker’s mindset. You shouldn’t do any of the following, in order to save time and have more confidence in your workflows, but maybe they’ll inspire you to pull some hacky mischief yourself.

Copy as PNG

I’ve recently been building NestedName, a site to find domain hacks with your name. For example, it’d help me find the domains emil.ie and emilie.ma, the latter of which I do own. I’ve been planning for a soft launch and some outreach, so I’ve been working on an animation for Twitter describing NestedName and its features as a little intro reel.

The sane thing to do would be to use a video recorder app, as my friend quickly pointed out. There are several tools that record your screen, then zoom in on your cursor and where you’re typing to generate smooth, polished demo videos. But I didn’t know of these beforehand, and anyways I wanted some specific animations in my video, so I thought I’d just hand-animate it.

All my design for NestedName happens in Figma, so I thought I’d look into plugins for animation within Figma so I could easily reuse design assets. I’d already abused Figma for presentations before, so I knew animations wasn’t too far of a stretch. I was able to find a couple, including Jitter.video, Figmotion, and Motion. Jitter’s free offering had a watermark, Figmotion was nice but wasn’t able to animate between several frames, and Motion, while being the most intuitive, had a free tier with a 2s export limit, far too low for my ~20s goal. These tools also all relied on manually creating keyframes and adjusting animation durations, which was tedious and frustrating.

Canva is another design tool I’ve used. It boasts an impressive selection of community-created templates, useful for when I need an impressive-looking poster for a class without wanting to start from scratch. They also have a video editing tool and basic animations available: think ‘pan’, ‘appear’, and ‘rise’. After fighting various Figma plugins for an afternoon, I decided to just roll with whatever Canva had available and try animating with Figma instead.

Canva’s free design tools are less powerful than Figma’s though, since they’re aimed at different markets. For one, Canva doesn’t allow you to import custom fonts if you’re not on their paid tier, and its font library, while extensive, also lacks the Google fonts that I’m using with NestedName. A good idea at this point would perhaps to go back to find an appropriate screen recorder so I’d be able to just record the NestedName interface and use Canva to edit on top of it. However, I decided a reasonable, rational, and clearly-thought-out alternative was to go into Figma, copy each text block as a PNG, and paste it into Canva as an image. I’d then animate the individual textblocks as if they were text elements, and stitch the final animation together with Canva. This required a lot of exporting and copy-pasting: if you’re ever in my boat, a good Figma shortcut to know is Ctrl-Shift-C for ‘copy as PNG’.

Canva’s animation interface is more intuitive than manual keyframes, as it’s mostly drag-and-drop and click-to-select for animations. This also means it’s less powerful unless you’re on the paid version, but even then the additional features are very limited.

  • For one, you can’t delay animations, so they all play in sequence at the start of the page. I wanted to delay some animations, though, so I’d duplicate the page in Canva, set the first page’s duration to be longer, and only apply the animation to the second page.
  • If I wanted to delay an animation after a page transition like a crossfade or wipe, I’d create an empty interstitial page, set it to have a short duration of 0.3s, then have my main animated page.
  • Canva’s animations on each page play in sequence following a heuristic from top down, from left to right. I had to rearrange some of my pages so the animation order would be correct, or otherwise use my workaround of creating several pages to animate one ‘visual’ page.
  • You can’t change the direction of certain animations on the free tier, so if you want to have an element fly in, it can only fly in from the left. Take this into consideration when laying out slides.

On the whole, the animation experience was quite straightforward, and I found the point-and-click interface to be a better workflow than manually creating keyframes. Even if it required a lot of hacky text rasterization and animation tool misuse, you can’t really tell in the final video. You can see the finished product here — while you’re there, feel free to follow and stay tuned for more updates!

GitHubDB

I wrote a static RSS feed aggregator a few years ago, and as part of that project I wanted to add bookmarks. However, I wanted to avoid setting up another server as much as possible, because I was already using up all my then-free Heroku credits on running the CORS proxy for the site. Of course, I realized that I could just add a few endpoints to the CORS proxy app to handle bookmarks as well, but I also wanted to avoid setting up my own authentication as much as possible, which would have required setting up MongoDB or another free database provider2. Too much effort, I reckoned.

Clearly, though, outsourcing authentication to a GitHub OAuth app was much easier. Then, I could just store my bookmarks as comments on a GitHub issue in the same private repo I already use to store my notes, so my bookmarks would be accessible from GitHub as well! There’s even prior art for the idea, though I came up with it separately.

Using GitHub issue comments to store and read bookmarks from is perhaps my most egregious misuse of GitHub, but I’ve also used it for sharing unlisted files and portfolios. Last year, I was applying to research assistant positions at my university and needed to submit a portfolio of prior work. I could have set up a separate unlinked portfolio page, but I also wanted to host several large demo videos, and while I could do that with Netlify, I wanted to send the portfolio ASAP rather than fiddling with styling another page. So, I threw everything into a private, unlisted Gist and sent the link to my professor. Little known fact: Gists can be cloned and committed to just like normal Git repos, so while the Gist interface doesn’t let you upload binary files directly, you can just push them from the command line.

Recently, I’ve also been considering using GitHub Mobile as a mobile text editor app for my blog posts. I make this arbitrary delineation between my time at home: ‘upstairs’ being work in my office, and ‘downstairs’ being time in the living room, maybe with a tablet. I’d like to better utilize some of my ‘downstairs’ doomscrolling time, so I’ve downloaded GitHub mobile and plan to use it to read through and edit some of my drafts, which are themselves committed into my private notes repo.

I have this concept of ‘free-driven development’: making software architecture or process decisions motivated purely on keeping everything within the free tier of service providers. Using GitHub as a free, private database was one such decision. I think this concept also gels well with the hacker’s mindset of finding workarounds to artificial pricing limits.

SMTP, Please

When I first bought the kewbi.sh domain, I wanted to set up email so I could send and receive emails from a nice hey@kewbi.sh address. Receiving email to this address was easy enough with mail forwarding from Namecheap, but sending mail was a little more complicated. I didn’t want to sign up for Google Workspace, which is paid, and I also didn’t want to switch to another email provider, which would also likely be paid.

However, there’s a relatively well-known workaround to send mail from another domain via the Gmail SMTP servers. It involves generating an app password for the Gmail account you want to use to send emails from, then selecting the Gmail SMTP servers in the configuration and and using that app password as authentication. You can read more about it here. I’m convinced this probably isn’t against ToS, but I’m also very aware it’s not supported and is likely unintended. Like with most of the other workarounds above, I’m relying on undocumented behaviour, which I realize could change any time, taking my whole workflow with it. But if that happens, I’ll just hack together another solution — no biggie.

Conclusion

Part of being a hacker is this ability and willingness to bend (software) boundaries and go beyond what you’re given at face value. A friend once said they had a thesis that the world is malleable, and I think that’s even more so for self-described hackers. We have the skills, or determination to pick up the skills, to navigate beyond what’s expressly presented: to misuse Canva, GitHub, Gmail, and more. Even if it’s janky, I think that hacky workarounds have their own charm.

An example of free-driven development in the real world: I recently soft-launched NestedName, a site to help you find unique domains with domain hacks and unusual TLDs. I think this blog’s domain (kewbi.sh) is pretty clean, and I own my full name as a domain (emilie.ma) as well. Quirky domains are something that I nerd out about often, so I wanted to make a site to help folks find them too — check it out, if you haven’t already.

I run NestedName for completely free, and there’s a fair bit of hackiness behind the scenes. For one, the frontend, dynamic open graph image generation function, DB, and backend are each hosted on separate providers (Cloudflare Pages, Vercel, Supabase, and Fly.io, respectively). For another, I was using an awkward heuristic from the free Cloudflare DNS API to determine if a domain was taken, which proved to be insufficient, as I kept receiving messages about during launch. None of the code follows good design patterns, and when adding new features I try to bodge them in with as little refactoring as possible. It’s a fairly small codebase, so it’s been feasible so far, and it’s been nice not to have to think too hard and just keep shipping.

I find myself dealing with perfectionism and a desire to ‘do things right’ sometimes. A little mischief around what’s possible to make happen tends to take the edge off some of that navel-gazing. I think the hacker mindset embodies this almost rebellious attitude towards the prescriptivism and limits of software: it brings some of the fun back to building. It’s just plain fun to dive deep into odd backward compatibility details or be proud of yourself for figuring out a complicated, multi-step, workaround not knowing that another tool exists. As corporate, walled-garden software has grown entrenched in every aspect of our lives, I think we need a little more hanky-panky and creativity in how we (mis)use it, and I hope this collection of mischievous ideas has inspired you to try breaking some limits and exploiting little-known features yourself2.


  1. I’ve been reading about design patterns lately for my Software Engineering class, and I take no small amount of satisfaction in my small rebellions against the prescribed best practices. Cohesion, avoiding duplicated code, and refactoring magic numbers? Who needs that? ↩︎

  2. Assuming good faith, of course! ↩︎ ↩︎


‹ go back