hi im josh
it is 2026 - january
"${year} is going to be my year for sure"
11 April 2026

I'm going to try and keep track of all the Stuff* I do this year. I want to be able to look back from the smouldering ruins of 2027 and think it wasn't all that bad and at least I got some things done.

*stuff meaning hobby gamedev, tools, fun stuff that I publish to the world.

January

Peachy update

I'd been neglecting my little Aseprite LOVE library for a while and I was never quite happy with how it worked.

I spent a little time this month fixing it up and addressing some issues and quirks:

Aseprite sheet combiner

I had this code (or code like it) sitting around for a while but I finally cleaned it up and made it usable.

What does it do? Past me wrote this excellent summary in the readme:

A utility for combining multiple Aseprite files into a single spritesheet with corrected animation frame positions.
This lets you have loose .aseprite files for your assets but still combine them into one spritesheet (for runtime draw performance via batching).

I can have an artist (or just me :)) work on individual .aseprite files without having to worry about clobbering atlases or merging big (binary) Aseprite files. Everything is nicely contained until it needs to be spritepacked as part of the build process.

Incidentally a big improvement I want to make to this is properly spritepacking, it relies on Aseprite's packing atm. Might just be able to crop and trim but then slices etc. won't make sense? I think. Will look into.

The Newest York

I had a realisation that New York might not be the Newest York and wrote a bit of code and did a bit of manual research into figuring it out and published it as a blogpost/largely just rambling mess.

spoilers: the newest one isn't new york and honestly they should hand over the title.

lua-ioc container

This is staggeringly unhelpful and goes against everything Lua stands for but I wrote a little IoC container (library???) in/for it over a lunch break.

You write code like this:

local ioc = Container(IoCMode.DependencyInjection)
ioc:register(LogService, LogService, BindingType.Singleton)
ioc:register(DoSomethingService, DoSomethingService, BindingType.Transient)
ioc:register(ExtraParamService, ExtraParamService, BindingType.Transient)

local doSomethingService1 = ioc:resolve(DoSomethingService)
doSomethingService1:doSomething()
doSomethingService1.logService:log("Hello from doSomethingService!")

and your wildest dependency injected dreams come true. It just works!

Unfortunately if you look under the hood it's extremely cursed and I don't recommend actually using it.

You write your constructor functions like this:

---Creates a new DoSomethingService instance.
---@param container Container The IoC container to resolve dependencies from.
function DoSomethingService.new(logService, extraParamService)
    local self = setmetatable({}, DoSomethingService)
    self.logService = logService
    self.extraParamService = extraParamService

    return self
end

and then the container will resolve the dependencies automagically using Lua's debug library:

---Resolves dependencies for a given implementation.
---@param constructor function The constructor for the implementation to resolve dependencies for.
---@return table ... The resolved dependencies.
function Container:resolveDependencies(constructor)
    local params = {}
    local info = debug.getinfo(constructor, "u")

    for i = 1, info.nparams do
        local parameterName = debug.getlocal(constructor, i)
        assert(parameterName, ("Failed to get parameter name for parameter %d of %s"):format(i, tostring(constructor)))

        local className = transformArgumentNameToClassName(parameterName)
        local type = self:getTypeByName(className)

        table.insert(params, self:resolve(type))
    end

    return unpack(params)
end

for those of you not so moonly inclined this:

So this is obviously:

For fun I also added a more sane (perhaps) service locator mode which doesn't use the debug stuff at all:

---Creates a new DoSomethingService instance.
---@param container Container The IoC container to resolve dependencies from.
function DoSomethingService.new(container)
    local self = setmetatable({}, DoSomethingService)
    self.logService = container:resolve(LogService)
    self.extraParamService = container:resolve(ExtraParamService)

    return self
end

It's essentially the same except the only thing injected is the IoC container itself and the constructors are responsible for fetching their dependencies. Maybe a bit more sane, but on the other hand it turns it into a service locator...

ennui

I started working on a UI library for LOVE, just like everyone else.

I haven't released this yet but I've posted about it a few times in a LOVE discord.

It's fairly complete at this point:

The inspector I'm particularly proud of (though it's still early). It works as a completely separate ennui Host so you can toggle it easily and change which Host you're debugging. It can even inspect itself!

This site

I finally posted something. You're reading it.

Got some styling on it and everything.

til next time

anyway thats everything until february when I hopefully release ennui :)