Elm Connections Retro

If you follow my blog, you would’ve noticed several videos of me coding up a Connections clone in Elm. I did this as a bit of an experiment, to see if I’d would be interested in screen-casting my coding sessions, and if anyone else would be interested in watching them. I also wanted to see if hosting them on a platform that’s not YouTube would gain any traction.

So far, I’ve received no takers: most videos have received zero views so far, with the highest view count being three. I’m guessing part of the reason is that the audience for this sort of stuff is just not there, or maybe it is but they’re spending all their watch-time on YouTube and Twitch. Building an audience on a platform like PeerTube might be feasible, but it’ll be quite a slog to fight for oxygen from these juggernauts.

But I also have to know that it’s unreasonable of me to expect any decent view numbers after just seven videos, especially after the first seven videos from someone who’s starting from scratch. Much like growing an audience for anything else, it’s just one of those things I need to work at, if I want it. Part of me is not sure that I do want it. And yet, the other part of me is seeking out posts about coders streaming on Twitch. So maybe that desire is still there.

Nevertheless, I’m glad I took on this small summer project. I had a chance to experiment with Elm, which was much needed exercise of my programming skills. I also had a chance to try out video production and editing using DaVinci Resolve, and I had a play around with PeerTube which… well, who can resist playing around with software? So although I didn’t get the banana1, at least I managed to compost the peel.

Anyway, on to the retro. Here are a few things I’ll need to keep in mind the next time I want to attempt this (it’s written in the second person as I’m writing this to myself):

Recording

  • Do a small recording test to make sure you’re setup is working. That last thing you want is to have a 30 minute recording with no audio because you forgot to turn on your mic.
  • Drink or sneeze while recording if you need to but make sure you stop moving things on the screen when you do, especially the mouse. That would make it easier for you to trim it out in the edit. This also applies when you’re thinking or reading.
  • When you do restart after drinking or sneezing, avoid repeating the last few words you just said. Saying “I’m going to be… (sneeze)… going to be doing this” makes it hard to cut it from the edit. Either restart the sentence from the beginning (“I’m going to be… (sneeze)… I’m going to be doing this”), or just continue on (“I’m going to be… (sneeze)… doing this”).
  • Also, just before you restart after drinking or sneezing, say a few random words to clear your voice.
  • Avoid saying things like “um” and “ah” when you’re explaining something. I know it’s natural to do so, so if you can’t avoid it, stop moving when you do so they can be edited out.
  • Don’t sigh. It makes it seem like you’re disinterested or annoyed.
  • Narrate more. Longs stretches of keyboard noises do not make for interesting viewing.
  • Saying things like “let’s change this” can be improved upon by saying why you’re changing “this”. Viewers know that you’re changing something — they can see it. What they can’t see is your thinking as to why it’s being changed at all.
  • Try to keep the same distance from the mic, and speak at the same volume, especially when saying things in your “thinking” voice (it tends to be a little quiet).
  • If you think the editor font is the right size, make it two steps larger.

Editing

  • Showing that you’re thinking or reading is fine, but don’t be afraid to trim it down to several seconds or so. Long stretches of things not happening on screen looks a little boring.
  • Proofread any titles you use. Make sure you’ve got the spelling right.
  • Try not to get too fancy with the effects to show the passage of time. Doing so means you’ll need to recreate it the same effects for subsequent videos. Less might be more here.
  • Learn the keyboard shortcuts for DaVinci Resolve. Here are some useful ones:
    • m: Add new marker.
    • Shift+Up, Shift+Down: Go to previous/next marker (only works in the edit section though? 🤨).
    • Cmd+\: Split the selected clip.
    • Option+Y: Select all clips to the right of the playhead (useful when trimming stuff out and you need to plug the gap).
  • Your screen is not big enough for 1080p recordings. Aim for 720p so that the video will still be crisp when exporting (a 16:9 video intended for 720p will need a capture region of 1280x720)

Publishing

  • You don’t need to announce a new episode as soon as it’s uploaded. Consider spacing them out to one or two a week. That would make it less like you’re just releasing “content”.
  • Aim to publish it around the same time, or at least on the same day. That should give others an expectation of when new episodes will be released.
  • Put some thought into the video poster. Just defaulting to the first frame is a little lazy.
  • If you’re using PeerTube, upload the videos as private first and don’t bother with the metadata until the upload is successful. Then go back and edit the metadata before making the video public (changing a video from private to public will send out an ActivityPub message). That way, there’s less chance of you loosing metadata changes if the upload were to fail.

  1. The banana here is anyone taking an interest in these videos; and I guess releasing Clonections itself? But not doing so is a conscious choice, at least for now. ↩︎

If someone asked me what sort of LLM I’d used for work, I wouldn’t go for a code assistant. Instead, I’d have something that’ll read my Slack messages, and if one looks like a description of work we need to do, it’ll return the Jira ticket for it, or offer to create one if I haven’t logged one yet.

On Go Interfaces And Component-Oriented Design

Golang Weekly had a link to a thoughtful post about interfaces. It got me thinking about my use of interfaces in Go, and how I could improve here. I’ve been struggling with this a little recently. I think there’s still a bit I’ve got to unlearn.

In the Java world, where I got my start, the principal to maintainable systems was a component-based approach: small, internally coherent, units of functionality that you stick together. These units were self contained, dependencies were well defined, and everything was tested in isolation. This meant lots of interfaces, usually defined up front before you even start writing the code that uses them.

I can see why this is popular. The appeal of component design is reuse: build one component for your system and you can use it in another system. I think the principals come from electrical engineering, where you build isolated components, like a transistor or an IC, that when put together produces the working electrical system. So it’s no surprise that this was adopted by the Java and object orientated community, which took such ideals of reuse to extreme levels, as if you could build a system out of the components of another system (this seemed like the whole motivation behind J2EE). Nevertheless, it was a patten of design that appealed to me, especially my sense of coming up with grand designs of abstraction layers.

But recently, I’ve been experiencing a bit of loss in religion. As the post points out, the idea of component design have merit in principal, but they start to break down in reality. Code reuse isn’t free, and if taken too far, you end up spending so much effort on the costs of abstraction (rewriting models, maintaining interfaces, dealing with unit test mocks) for very little benefit. Are you really going to use that Go service in the “catalogue manager” in something else?

So I agree with the post but I come away from it wondering what an alternative to component design actually looks like. I’m still trying to figure this out, and it might be that I’ll need to read up on this some. But maybe it’s to take the idea of self contain units, and throw away the imagined idea of reuse. In concrete terms, ditch the interfaces and replace them with direct method calls.

As for testing, maybe focus less on testing individual units and more of the system as a whole. I’m already onboard with the idea of not mocking out the database in unit tests, but I’m starting to come around to the idea of a “unit” being more than just a single type tested in isolation. I’m guessing this is inevitable as you throw away your interfaces and start depending on other types explicitly. That is, until you begin seeing areas where reuse is possible. But maybe this gets back to the original idea of Go interfaces: they’re more discovered than prescribed.

Anyway, it might be worth trying this approach for the next personal project I have in mind. Of course, that’s the easy part. Finding a way to adopt this pattern at work would be interesting.

So apparently tonight’s earworm is lesser known songs from Men At Work’s “Business As Usual” album, like People Just Love To Play With Words, I Can See It In Your Eyes, and Be Good Johnny. 🎵

In the end it took significantly more time to write about it then to actually do it, but the dot product approach seems to work.

🎥 Elm Connections #7: Deploying

In which I round out the series by deploying Clonections to Netlify.

Detecting A Point In a Convex Polygon

Note: there are some interactive elements and MathML in this post. So for those reading this in RSS, if it looks like some formulas or images are missing, please click through to the post.

For reasons that may or may not be made clear lately, I’ve been working on something involving bestagons. I tended to shy away from things like this before, mainly because of the maths involved in tasks like determining whether a point is within a hexagon. But instead of running away once again from things more complex than a grid, I figured it was time to learn this once and for all. So off I went.

First stop was Stack Overflow, and this answer on how to test if a point is inside a convex polygon:

You can check that easily with the dot product (as it is proportional to the cosine of the angle formed between the segment and the point, if we calculate it with the normal of the edge, those with positive sign would lay on the right side and those with negative sign on the left side).

I suppose I could’ve taken this answer as it is, but I know if I did, I’d have something that’ll be little more than magic. It’ll do the job but I’d have no idea way. Now like many, if I can get away with having something that works without me knowing how, I’ll more likely to take it. But when it comes to code, doing this will usually comes back to bite me in the bum. So I’m trying to look for opportunities to dig a little deeper than I would in learning how and why it works.

It took me a while, and a few false starts, but I think I got there in the end. And I’d figured it would be helpful for others to know how I came to understand how this worked at all. And yeah, I’m sure this is provable with various theorems and relationships, but that’s just a little too abstract for me. No, what got me to the solution in the end was visualising it, along with attempting to explain it below.

First, let’s ignore polygons completely and consider a single line. Here’s one, represented as a vector:

A vector drawn on graph paper pointing to the top-right

Oh, I should point out that I’m assuming that you’re aware of things like vectors and trigonometric functions, and have heard of things like dot-product before. Hopefully it won’t be too involved.

Anyway, we have this line. Let’s say we want to know if a specific point is to the “right” of the line. Now, if the line was vertical, this would be trivial to do. But here we’ve got a line that’s is on an angle. And although a phrase like “to the right of” is still applicable, it’ll only be a matter of time before we have a line where “right” and “left” has no meaning to us.

So let’s generalise it and say we’re interested in seeing whether a point is on the same side as the line’s normal.

Now, there are actually two normals available to us, one going out on either side of the line. But let’s pick one and say we want the normal that points to the right if the line segment is pointing directly up. We can add that to our diagram as a grey vector:

That same vector pointing to the top-right, with a normal originating from the same origin pointing to the bottom-right

Now let’s consider this point. We can represented as a vector that the shares the same origin as the line segment1. With this we can do all sorts of things, such as work out the angle between the two (if you’re viewing this in a browser, you can tap on the canvas to reposition the green ray):

That same vector and normal, now with an additional line coming from the origin drawn rotated 48° clockwise from the original vector

This might give us a useful solution to our problem here; namely, if the angle between the two vectors falls between 0° and 180°, we can assume the point is to the “right” of the line. But we may be getting ahead of ourselves. We haven’t even discussed how we can go about “figuring out the angle” between these vectors.

This is where the dot product comes in. The dot product is an equation that takes two vectors and produces a scalar value, based on the formula below:

a b = a x b x + a y b y

One useful relationship of the dot product is that it’s proportional to the cosign of the angle between the two vectors:

a b = | a | | b | cos θ

Rewriting this will give us a formula that will return the angle between two vectors:

θ = cos -1 ( a b | a | | b | )

So a solution here would be to calculate the angle between the line and the point vector, and as long as it falls between 0 and 180°, we can determine that the point is on the “right” side of the line.

Now I actually tried this approach in a quick and dirty mockup using JavaScript, but I ran into a bit of an issue. For you see, the available inverse cosign function did not provide a value beyond 180°. When you think about it, this kinda makes sense, as the cosign function starts moving from -1 back to 1 as the angle is greater than 180° (or less than 0°).

But we have another vector at our disposal, the normal. What if we were to calculate the angle between those two?

That same vector and normal, and the additional line coming from the origin drawn 136° anti-clockwise from the normal, with text indicating that the dot product is -47500

Ah, now we have a relationship that’s usable. Consider when the point moves to the “left” of the line. You’d notice that the angle is either greater than 90° or less than –90°. These just happen to be angles in which the cosign function will yield a negative result. So a possible solution before is is to work out the angle between the point vector and normal, take the cosign, and if it’s positive, the point will be on the “right” side of the line (and it’ll be on the “left” side if the cosign is negative).

But we can do better than that. Looking back at the relationship between the dot product and the angle, we can see that the only way this equation could be negative is if the cosign function is negative, since the vector magnitudes will always return a positive value. So we don’t even need to work out angles at all. We can just rely on the dot product between the point and the normal.

And it’s here that the solution clicked. A point is to the “right” of a line if the dot product of the point vector and the “right”-sided normal is positive. Look back at the original Stack Overflow answer above, and you’ll see that’s pretty much what was said there as well.

Now that we’ve got this working for a single line, it’s trivial to extend this to convex2 polygons. After including all the line segments, with the normals pointing inwards, calculate the dot product between each of the normals with the point, and check the sign. If they’re all positive, the point is within the polygon. If not, it’s outside.

A hexagon drawn in the centre of graph paper with normals and lines originating at each vertex and converting at a single point located in the centre of the hexagon. The lines indicating a positive dot product for each one and that the point is within the hexagon

So here’s an approach that’ll work for me, and is relatively easy and cheap to work out. And yeah, it’s not a groundbreaking approach, and basically involved relearning a bunch of linear algebra I’ve forgotten since high school. But hey, better late than never.


  1. Well, technically these vectors should be offset from the global origin, but they’re translated in these plots for demonstration purposes. ↩︎

  2. I’m not sure if this is applicable to concave polygons, where the angle between segments can go beyond 180°. ↩︎

So I guess today’s beginning with a game of “guess the secret password requirements.” 😒

A macOS login reset password modal, partially filled in, with a prompt saying that 'your password does not meet the requirements of this server' without saying what the requirements are.

🎥 Elm Connections #6: Fetching And JSON Decoding Puzzles

In which I use Elm’s HTTP client and JSON decoder to fetch puzzles from an external resource.

For some reason, Android’s default setting for message notifications in Do Not Disturb is to notify on all messages. This, to me, seems like it defeats the purpose of DND.

You can turn it off or change it by going to Settings > Notifications > Do Not Disturb > People > Messages.

UI screenshot of message notification preferences in Do not Disturb with contacts selected.

Re-reading Cory Doctorow’s post about the enshittification of TikTok. A bit coincidental, as a YouTuber I follow recently stated that he had to cut down on videos and look for work because YouTube’s push for Shorts has had an impact on revenue. Could this be how YouTube starts enshittifying?

🔗 Please, Own Your RSS Links

All you have to do is dream up a good URL at your domain and redirect it to the feed’s URL provided by whatever service you use to host your stuff. And then that’s where you tell folks to subscribe.

It’s easy to forget (like I do) that there’s nothing magical about an RSS feed. It’s just one more thing served by HTTP at a URL. And thus, is useable with the real magic here which is HTTP redirects.

This is a brilliant idea. The only thing I’ll add is just to make those RSS feeds discoverable.

Discovered a few days ago that I was completely out of coffee beans. So after getting some emergency beans from the supermarket, I ordered a kilo of my default: Primo Fair Trade Organic.

And yes, part of the reason for this post is that I forgot the URL of this site.

Impressed with the new table editor added in Obsidian v1.5. Tables were Obsidian’s Achilles’ heel so it’s great to see them improve this. There’s a small bug when opening a new row, where focus is lost after I press Enter and I can’t just start typing in the new row. But otherwise, good job.

Edit: Ah, the bug might be because I had the “Advanced Table” plugin enabled. Turning that off seems to have fix it, and I don’t loose focus anymore.

Summer break over, back to work today. Though I’m glad I took an extra day of leave this time. Pushed the return to work feeling from “it’s too soon” to “okay, I’m ready to go back now.”

🎥 Elm Connections #5: Option Shuffling

In which I use Elm’s random number generator to shuffle the options.

The sun was peaking through the clouds this morning that for a minute I wondered whether it was worth taking my umbrella to the cafe this morning. I’m glad I did, because storms developed a few minutes ago and now it’s raining. For once, I’m ahead of the weather. ☔️

Can a Single Line Or Even a Single Word Be Considered a Legitimate Blog Post?

Yes.

Of Lemons And Modern Software

I found myself nodding my head throughout Alex Russell’s post The Market For Lemons:

The complexity merchants knew their environments weren’t typical, but they sold highly specialised tools as though they were generally appropriate. They understood that most websites lack tight latency budgeting, dedicated performance teams, hawkish management reviews, ship gates to prevent regressions, and end-to-end measurements of critical user journeys. They understood the only way to scale JS-driven frontends are massive investments in controlling complexity, but warned none of their customers.

He’s talking about JavaScript frameworks and their failure to produce performant web frontends. But it’s amazing how much of this also applies to the backend world as well. Replace SPA with micro-services, and React with Kubernetes, and you’ve pretty much nailed the problem in that space too.

I’m willing to believe the the trap we find ourselves in is not entirely the fault of these vendors. I can picture a scenario where some dev got curious about React, used it for small project, thought it was the bees knees, and started sharing it with others. The tech was complex enough to be usable yet interesting, and had enough of a pedigree that it was easy to persuade others to adopt it (“Facebook and Google use this, and they’re bajillionaire serving half the world’s population. Why doubt tech from them?”)

Eventually it receives mainstream adoption, and despite the issues, it’s the default choice for anything new because of two characteristics. The first is the sunk cost associated with learning these frameworks, and justifying the battle scars that come from fighting with them.

The second is the fear of rework. I suspect most organisation forget that many of these Big Tech firms started with what are effectively LAMP stacks. They had to replace it with what they have now because of user demand, and the decision was easy because the millions of requests they got per second were stressing their software.

And I suspect organisations today feel the need to skip the simple tech stacks and go straight to something that can serve those billions of users just to save themselves the perceived future need to rewrite the app. Sure, that server-side rendered web page handled by a single web server and PostgreSQL database is enough to serve those 100 DAU now, but someday that may grow to three billion users, and we need to be ready for that possibility now (even if it doesn’t materialise).

So I totally understand the mess we’re in now. But that doesn’t explain why these vendors went to such lengths promoting this tech in the first place. Why would they not be upfront about the massive investment they made to be effective with this tech? Why not disclose the reason why they specifically had to go to such lengths, rather than tout properties like “the DOM is slow” or “Amazon uses micro-services”?

I’d be curious to find out, but maybe later. I’ve got some Kubernetes pods I’ve got deal with first.

🎥 Elm Connections #4: Styling

In which I put away Elm for a bit to make the playfield look good (or at least, better than it was).