You are browsing as a guest. Sign up (or log in) to start making projects!

angelcube

@angelcube

Joined June 1st, 2026

  • 10Devlogs
  • 6Projects
  • 2Ships
  • 15Votes
Ship Changes requested

This is (somewhat) done—there's still an unresolved code smell, check the PR for details. I'm going to ship this now for the Free Sticker™, but stay tuned for the merge :)

Alternatively, reviewers, don't approve this until it gets merged. That's fine with me too!

To install this, clone the repository at the source code link below ⬇️, then pnpm build. Follow the Vencord docs @ installing your custom build to install! Then, go to Settings -> Plugins -> AudioScrobblerRichPresence, open the plugin settings, and configure as needed :)

  • 1 devlog
  • 5h
Try project → See source code →
Open comments for this post

5h 29m 45s logged

for more info check out the pull request thread

ListenBrainz Rich Presence in Vencord!

I’ve always been a fan of tools that track your listening activity. Last.FM is a website that does exactly that! Vencord—a client mod for Discord—included a plugin that showed your Last.FM “Now Playing” as a Rich Presence activity, and I used that for a while. However, I grew increasingly dissatisfied with how Last.FM handled metadata, which is to say, it didn’t. There would be like, five different entries for one song, six for one artist, and so on and so forth. Also, oftentimes Last.FM wouldn’t associate albums with tracks—that meant the track would have no album art, and it was really annoying to see a placeholder image show up on your Discord profile. The solution…?

ListenBrainz! A scrobbler application made by the same organization responsible for MusicBrainz, the database on everything song metadata. However, using ListenBrainz instead of Last.FM meant that I wouldn’t have my currently playing song show up on my Discord profile. Which obviously meant I gave up.

Just kidding, I wrote my own plugin: ListenBrainz Rich Presence for Vencord. That isn’t part of my Stardance time though, I wrote that janky mess all the way back in March. I submitted this plugin as a pull request to the Vencord repository, only to get this response:

Vendicated: Please merge this into the lastfm rpc plugin to avoid duplicating code

And I said, sure, I can do that—then proceeded to forget all about this project for a whole month. This week, Vendicated bumped the thread, asking whether I was gonna actually do it, and that’s where our story actually starts.

The idea: a unified scrobbler plugin (obviously) that accepts a variety of different backends for its status. Last.FM and ListenBrainz would be separate backends that the user could choose from. I would also make the whole fetching metadata feature a separate thing that the user could access from either backend, using MusicBrainz instead of the ListenBrainz lookup API (which requires a token now because of AI abuse.)

Here’s the thing, though—I thought I would make the backend selection an enum. However, a backend tends to be more of an object than just a string—it’s got an ID, a name, a website, and tons of other properties that all have to be expressed. My initial implementation was janky as shit:

interface ScrobblerBackend {
    name: string,
    id: string,
    url: string,
    userProfilePath: string;
}

const ScrobblerBackends: Map<string, ScrobblerBackend> = new Map([
    [
        "lastfm",
        {
            name: "Last.FM",
            id: "lastfm",
            url: "https://www.last.fm/",
            userProfilePath: "/user"
        }
    ],
    [
        "listenbrainz",
        {
            name: "ListenBrainz",
            id: "listenbrainz",
            url: "https://listenbrainz.org/",
            userProfilePath: "/user"
        }
    ]
]);

what the hell dawg 😭

This is partially attributed to my fairly weak understanding of TypeScript as it relates to object-oriented programming—I didn’t know how classes and inheritance actually worked. I learned OOP in Java and C#, okay 😭 this was all still kinda new to me!

Obviously Vendicated caught and corrected me real quick there, and I learned that yes, inheritance is a thing. Classes can inherit interfaces. Isn’t that crazy.

We construct this interface…

export interface ScrobblerBackend {
    name: string,
    id: string,
    url: string,

    fetchTrackData(username: string, apiKey?: string): Promise<TrackData | null>;
    getUserURL(username: string): string;
}

…and then we do export class ListenBrainzScrobbler implements ScrobblerBackend. Woohoo. Yippee.

0
0
3
Open comments for this post

3h 36m 11s logged

DEVS OF STARDANCE, I need YOUR HELP!

Setting wallpapers for multiple monitors on macOS is tediously hard! There’s gotta be a Swift application in the middle accepting this call, so…

Would you rather:

  • a Swift library bundled within the (TypeScript) application to interact with Cocoa and handle setting the wallpaper
  • a dependency on another CLI tool like desktoppr for this?

LET ME KNOW IN THE COMMENTS! Or something. I’m not a YouTuber

1
0
14
Open comments for this post

1h 42m 8s logged

Today I learned that the CIEDE2000 algorithm never worked in the first place and was outputting numbers opposite to on the colour wheel. Why? I have no clue and I don’t intend to find out, because I’ve decided I’ve had enough of typing in sine and cosine and was just going to use the library colordx instead. The whole project was refactored, including the analyze API, so if you’ve had an index file generated previously make sure to regenerate it!

1
0
8
Open comments for this post

4h 0m 45s logged

I just spent four hours doing genuinely the most annoying thing on the planet: building for Windows.

I found a neat little Stack Overflow thread that seemed to do what I wanted it to: write a PowerShell script that could set the wallpaper on Windows machines. Not too shabby, I thought. I could probably ask one of my friends to test on their machine. It wouldn’t take too long, maybe half an hour at max.

Boy, was I wrong.

I forced the poor bloke to install ImageMagick, then sent him a CI build. We gave the program a test run, and I ran into my first real challenge: paths.

Oh my God, I hate Windows paths.

Nothing would work—making folders, writing files, any of that until I replaced every single forward slash with Deno’s fromFileUrl. And being a person with not so great memory, I sent over at least five builds before I fixed every single path in the way. Honourable mentions include:

  • I mistook configPath for cachePath
  • fromFileUrl("C:/..") because that is definitely a file:/// URL
  • path.join(`${imageDir}, ${image.name}`)

Part 2: PowerShell. I thought (:clueless: ) that zx would run on PowerShell out of the box. Nope! It depends (and it actually says this in the README too) that it depends almost exclusively on Bash and Bash escaping. There’s a usePowerShell() command, but I genuinely don’t have the energy right now to figure out the refactors required to make that work—on someone else’s machine.

So I make the guy install Git Bash, and also Deno so I can just send him diffs instead of having to send the Full Deno Runtime™ every time I had a new build. At this point, rainwall-analyze finally works (yay!) and dumps the correct paths to the cache folder. We run rainwall-apply, I find four new cases of paths I did not fix, and I go back into VSCode hell.

Part 3: the actual PowerShell script. Usually, on Linux, I just use Bash’s eval whenever I need to run something enclosed in a string, because zx will automatically quote and escape everything you’ve passed in through a variable. There’s no eval with PowerShell, so I do an insane amount of googling. At this point, I send him a total of twelve diffs just to test to see what the heck is going on. We eventually settled on using Deno.Command instead of zx for this one, running pwsh C:\path\to\script C:\path\to\image. Later, after I logged off for the night (at 2:00! I thought I’d be in bed by 22:00!), I did realize one thing—while we were installing Deno, I saw that they had an install script irm https://deno.land/install.ps1 | iex. I figured a much, much easier way would’ve been to just run 'C\path\to\script' | iex instead, but my brain at that insane hour was not having it.

That’s the whole Windows saga, for now. I still have to adjust the lightness curves a little bit, but other than that, we should be nearing completion. Today, I want to test the script with my macOS friend… provided she wakes up earlier than 21:00. Almost there!

0
0
4
Open comments for this post

2h 33m 48s logged

This is stupid. Literally everything I’ve been doing today was exclusively trying to set up the file paths, getting the types, and making sure the game doesn’t crash. I’ll try again tomorrow, and hopefully things go smoother then…

I love the Source Engine :)

0
0
3
Open comments for this post

55m 38s logged

There I was, scrolling through my Discord servers, when I saw this link: https://www.keithcirkel.co.uk/whats-my-jnd/
It was a small program to calculate the Just Noticeable Difference between two colours, or the JND. Reading the blogpost linked, it mentioned an algorithm called the “CIEDE2000.” And that’s when it hit me—the CIE is a real organization. Of course they would have a formula to calculate the similarities between two colours. That meant that I didn’t have to iterate through my array of wallpaper colours and do simple less-than checks—using CIEDE2000 would be much more accurate. Thankfully, there’s an npm library for this: color-diff. (I was NOT about to implement all that myself.) I also added a bit of randomization to the algorithm, so that should ensure you’re not getting the same wallpaper all the time if you live somewhere where there’s consistent weather patterns.

All in all, this probably constitutes a reship, doesn’t it? I’ll probably be tweaking the algorithm slightly as I continue to use it, so stay tuned…

1
0
10
Ship Changes requested

**🌈 rainwall is done!**
This is a CLI utility that uses the weather outside to select a matching image as your wallpaper.
This was my first time using Deno, and I feel like it's got its pros and cons. I learned so much in manipulating the actual OS through TypeScript, and I feel much more confiident in it as a scripting language now!
I wasn't able to test on any other architectures, so please submit feedback as needed!
You can download rainwall via [JSR](https://jsr.io/@angelcube/rainwall/), [GitHub](https://github.com/weightedangelcube/rainwall/releases), or with the [PKGBUILD](https://raw.githubusercontent.com/weightedangelcube/rainwall/refs/heads/main/PKGBUILD)!

  • 4 devlogs
  • 11h
Try project → See source code →
Open comments for this post

2h 16m 21s logged

Dumping all the sun data times to ensure the algorithm’s as accurate as possible! (-0.833 is the angle of the sun at sunrise/sunset.)

0
0
7
Open comments for this post

2h 47m 53s logged

Done!!! YEAH! I had to reimplement the output so that it wouldn’t sort into categories, as that turned out to be harder for the final algorithm to find a matching wallpaper. Currently hueRange is unimplemented as I’m not super sure how to go about it, and I’ve got to clean up the easing function code somehow, but other than that, this should be pretty much ready to ship!

0
0
4
Open comments for this post

4h 7m 58s logged

Been formatting this into a full Deno project from an isolated zx idea. Never done anything config-related before, so making something to read/write the config was pretty cool.

A lot of writing the analysis code was processing the bash output into something manipulate-able with TypeScript. That was annoying as shit—splitting, filtering, mapping… I never want to process text again in my entire life.

Something really unexpected had to be the floating point errors I found in the loop. I was thinking there had to be a better way to populate the better array in the first place, but there didn’t seem to be, so there I was doing let i = 0, i < 1, i += 0.1 and fighting for my life against 0.60000001. I eventually got so tired of it I just made it a percentage out of 100% instead. Surely this won’t bite me in the back later :)

We’ve got cloud cover, shortwave radiation, and sun altitude with the help of Open-Meteo and suncalc. They’re going to map to parameters chroma, lightness, and hue respectively! This’ll be fun….

0
0
4

Followers

Loading…