Sign in

transmutrix

@transmutrix.bsky.social
2K followers 2.7K following 1.8K posts

🎮 programmer / game dev / game preservationist 💻 transmutrix.com 🕹️ transmutrix.itch.io Contact: 💌 transmutrix@hey.com

PostsRepliesMedia
transmutrix @transmutrix.bsky.social · 27/09/2026
blasting through making a buncha stuff today! wish me luck at finishing on time ;u;
a colorful, dark environment with a large wall with a closed door, and a courtyard with a statue of a cloaked figure holding something under their cloak
2130
transmutrix @transmutrix.bsky.social · 24/09/2026
haven't posted a clip of priiism in a while, been wicked busy! but things are shaping up. i have a LOT to do this week >.< no audio because i didn't wanna open OBS at the time lol
090
transmutrix @transmutrix.bsky.social · 23/09/2026
hell yeah! it's so pleasant
(fn []
 (lib.lock-input)
 (let [player (lib.player?)]
  (player.aseprite:play "sleep")
  (do
    (local audio (require :g.systems.audio))
    (audio.play-stream-looped "data/sounds/ambience/crickets.ogg" 0.4))
  (camera.on-level-loaded)
  (lib.wait 2)
  (let [lamp? (lib.entity? :08ea5750-96d0-11f1-bf9f-fdcdda05203b)]
   (when lamp? (lamp-trails.light! lamp?)))
  (lib.wait 1)
  (player.aseprite:play "wake")
  (lib.wait 3)
  (player.aseprite:play "idle")
  (lib.unlock-input)
  (lib.say "WASD / Arrows")))
010
transmutrix @transmutrix.bsky.social · 21/09/2026
me encouraging more people to get into niche programming languages, use an ancient piece of crap phone, divest from major platforms, and in general become weirder and less palatable to society at large
Twin Peaks "Shovel your way out of the shit" screenshot
1325
transmutrix @transmutrix.bsky.social · 15/09/2026
>you walk into the Happy Mask Shop on November 5 and see this
Dick Tremayne from Twin Peaks, smiling his saccharine smile
0111
transmutrix @transmutrix.bsky.social · 14/09/2026
i finally got around to adding map transitions! now i can start making the actual game and not just "Debug Room With Vibes"
0190
transmutrix @transmutrix.bsky.social · 13/09/2026
a secret wall to a secret path
0141
transmutrix @transmutrix.bsky.social · 10/09/2026
here's how the player "prefab" is set up in my little (overthought) scheme, currently.
(local util (require :g.util))
(local components (require :g.components))

(fn [ent data]
  (components.add ent :transform {
    :x data.spawn-x
    :y data.spawn-y
    })
  (components.add ent :sprite {
    :path "data/images/player/anims.png"
    :off-x -10 :off-y -16
    :lit true
    })
  (components.add ent :aseprite {
    :path "data/images/player/anims.json"
    :default "idle"
    })
  (components.add ent :shadow {
    :size 30
    })
  (components.add ent :controllable)
  ent)
150
transmutrix @transmutrix.bsky.social · 10/09/2026
i finally got some sleep, and i'm finally getting started on the player controller for this game. currently the wall and tile occupant checks are just stubs, but the basic movement feels pretty good. ^^
1100
transmutrix @transmutrix.bsky.social · 09/09/2026
accidentally made the world's teeniest toriel
1120
transmutrix @transmutrix.bsky.social · 09/09/2026
i went ahead and made a system for it, so any future leak will be more obvious, and to see how much frame time i'm trading for my temporaries lol
120
transmutrix @transmutrix.bsky.social · 09/09/2026
then, i made my segment-point helper return 3 things: the nearest point, a normalized scalar along the segment (clamped), and the world coordinates distance for the point. using that, i updated the test to show touching the beam with a red line, and draw a gray line as a "shadow" cast from the mouse
140
transmutrix @transmutrix.bsky.social · 09/09/2026
next, i searched "how to find the nearest point on a line segment" because despite making games for ~20 years, i always forget basic vector geometry stuff and have to relearn it on every project! then i updated my test to run that check for each beam. doing this uncovered a mistake i had made.
150
transmutrix @transmutrix.bsky.social · 09/09/2026
first, i wrote a small helper to transform the mouse coordinates into screen space, because i'm doing a custom scale and overdraw thing for this game. i've written that sort of thing a lot, so it went easy. to test, i made a debug draw that made lines from the mouse cursor to every beam emitter
130
transmutrix @transmutrix.bsky.social · 09/09/2026
yesterday i was wiped out and didn't get to work a lot, but i did add this little effect when things pass in front of light beams ^_^ it's not perfect but i'll write about how it went
3244
transmutrix @transmutrix.bsky.social · 07/09/2026
in there you'll find a single file for each of those Lua dialects, and a super barebones LÖVE example of how they behave
150
transmutrix @transmutrix.bsky.social · 07/09/2026
this is the sort of thing i usually do with it. the yellow lines poking out of the moths/flies are their velocities, and the static yellow dots nearby are their "anchors" in the scene
160
transmutrix @transmutrix.bsky.social · 07/09/2026
something i use all the time, shown here in Fennel + Lua: 2d slerp ("clerp?"). it lerps between two vectors on a circle instead of linearly. i initially wrote a version of this in Nim for Wizwag a few years ago. it's handy for smoothly turning velocities and i use it for insects, birds, etc.
;; a 2d "slerp" ("clerp?") on a circle. works with non-unit
;;  vectors so that it's nice for something controlling both
;;  speed and angle with the same vector.
(fn util.v2-slerp [v1 v2 t]
  (let [a1 (math.atan2 v1.y v1.x)                      ; angle of v1
        a2 (math.atan2 v2.y v2.x)                      ; angle of v2
        ; shortest signed difference, wrapped to [-pi, pi)
        d (- (% (+ (- a2 a1) math.pi) (* 2 math.pi)) math.pi)
        a (+ a1 (* t d))                               ; lerped angle
        r1 (math.sqrt (+ (* v1.x v1.x) (* v1.y v1.y))) ; magnitude of v1
        r2 (math.sqrt (+ (* v2.x v2.x) (* v2.y v2.y))) ; magnitude of v2
        r (+ r1 (* t (- r2 r1)))]                      ; lerped magnitude
    ; convert from polar back to v2
    {:x (* r (math.cos a)) :y (* r (math.sin a))}))

-- plain lua version
function v2_slerp (v1, v2, t)
  local a1 = math.atan2(v1.y, v1.x)
  local a2 = math.atan2(v2.y, v2.x)
  local d = ((((a2 - a1) + math.pi) % (math.pi * 2)) - math.pi)
  local a = (a1 + (t * d))
  local r1 = math.sqrt(((v1.x * v1.x) + (v1.y * v1.y)))
  local r2 = math.sqrt(((v2.x * v2.x) + (v2.y * v2.y)))
  local r = (r1 + (t * (r2 - r1)))
  return {x = (r * math.cos(a)), y = (r * math.sin(a))}
end
2371
transmutrix @transmutrix.bsky.social · 07/09/2026
as usual, i've tried to make things easy on myself. here's how i add soft background lights to the environment in LDtk.
160
transmutrix @transmutrix.bsky.social · 07/09/2026
i spent yesterday evening putting up fencing and petting happy goats! it was soul food. anyway, here's my new prototype after ~6 solid days of work. there's no gameplay yet - the big worry on this project was the visuals, so i've prototyped them first. what do you guys think so far?
3170
transmutrix @transmutrix.bsky.social · 05/09/2026
it was supposed to look like *this*
030
transmutrix @transmutrix.bsky.social · 05/09/2026
actually, i forgot the divisor (2) - i found the mistake by doing uh, this in a scratch file '^^
scale-x (+ 
            scale (* 
                        0.25 (/ 
                                (+ 
                                    1 (math.sin 
                                                (* 
                                                    math.pi 
                                                    2 
                                                    (+ 
                                                        (/ i 6) 
                                                        (love.timer.getTime)
                                                    )
                                                )
                                      )
                                )
                              )
                  )
        )


scale-y (+ 
            scale (* 
                        0.8 (/ 
                                (+ 
                                    1 (math.cos 
                                                (* 
                                                    math.pi 
                                                    2 
                                                    (+ 
                                                        (/ i 6) 
                                                        (love.timer.getTime))))))))
110
transmutrix @transmutrix.bsky.social · 05/09/2026
messed up a paren placement, looks kinda cool
170
transmutrix @transmutrix.bsky.social · 05/09/2026
moar fennel: i'm trying to get better at using the niceties the language provides ^^
; original
(let [el-typ (string.match typ "<(.+)>")
      items []]
  (each [_ el (ipairs val)]
    (table.insert items
      (load-single-field el-typ el grid-size)))
  items)

;rewrite
(let [el-typ (string.match typ "<(.+)>")]
  (icollect [_ el (ipairs val)]
    (load-single-field el-typ el grid-size)))
250
transmutrix @transmutrix.bsky.social · 03/09/2026
anyway, have another code snippet! today i finished loading LDtk files and wrote some naive tile rendering code. it's not shown here, but i'm doing my standard fix for avoiding tile seams and other pixel game problems, which is "draw everything to a scale=1 canvas and call it a day"

(fn get-tileset-cache [tex-path]
  (local tex (textures.get tex-path))
  (assert tex) ; TODO: be more graceful lol
  ; build a cache if one doesn't exist yet
  (when (not (. cache tex-path))
    (let [entry {}]
      ; load the texture, make a batch, etc.
      (set entry.batch (love.graphics.newSpriteBatch tex batch-max))
      (set entry.quads {})
      ; store the new cache
      (tset cache tex-path entry)))
  ; get and return the cache for this texture
  (let [entry (. cache tex-path)]
    ; set the texture on the batch (in case of reload)
    (entry.batch:setTexture tex)
    entry))

(fn get-tile-quad [ts-cache x y size tex-w tex-h]
  (util.get-or-default
   (util.get-or-default ts-cache.quads y []) x
   (love.graphics.newQuad x y size size tex-w tex-h)))

(fn draw-tile-layer [layer]
  ; TODO: camera system integration
  ; TODO: account for layer offsets in the cull & in draw!
  (let [cam-left 0
        cam-top 0
        cam-right (+ cam-left cfg.game-w)
        cam-bottom (+ cam-top cfg.game-h)
        ts  (. ldtk.tilesets layer.tileset-uid)
        tex (textures.get ts.img-path)]
   (when tex
    ; build up the sprite batch and draw it
    (let [ts-cache (get-tileset-cache ts.img-path)
         batch ts-cache.batch
         tex-w   (tex:getWidth)
         tex-h   (tex:getHeight)]
     (batch:clear)
     (each [_ t (ipairs layer.tiles)]
      (when (bounds-overlap? cam-left cam-top cam-right cam-bottom
                             t.x t.y
                             (+ t.x ts.tile-size)
                             (+ t.y ts.tile-size))
       (batch:add (get-tile-quad ts-cache
                                 t.src-x t.src-y
                                 ts.tile-size tex-w tex-h)
                  t.x t.y)))
     (batch:flush)
     (love.graphics.draw batch 0 0)))))
020
transmutrix @transmutrix.bsky.social · 03/09/2026
NO FUCKING WAY! So the claim is that someone *is* going around impersonating them to scam Steam keys. Every layer of this situation sucks lol
Please Note: If you are joining us here because you are a game developer/publisher and are inquiring if we reached out to you for a copy of your game(s) then it is not us. Please report the email as a phishing attempt. Our official email is easily found via Twitter/Twitch/YouTube. Anything originating from any other email address is not us.
250
transmutrix @transmutrix.bsky.social · 03/09/2026
here it is, the peak of human achievement in artificial intelligence. protein folding? no! cancer prediction? no! look at this! lay your pitiful human eyes on THIS! it's a PICKLE. a gippity pickle! funniest shit i've ever seen. 🙄
AI generated garbage that doesn't merit detailed examination
160
transmutrix @transmutrix.bsky.social · 01/09/2026
i can't abide not having live art-ing in a gamedev environment, so behold a really simple and naive LÖVE texture cache with hot-reloading in fennel. i haven't written much fennel yet so sorry if this is "C programmer tries lisp" sort of code (pls tell me, in fact)

(local cfg (require :g.config))
(local state (require :g.state))
(local util (require :g.util.misc))

(local textures (or state.textures
  {:cache {} :modtimes {} :to-reload {}}))
(set state.textures textures)

(fn file-modtime [path]
  (or (. (love.filesystem.getInfo path) :modtime) 0))

(fn load-texture [path]
  (let [(ok loaded-or-err) (pcall love.graphics.newImage path)]
   (if ok
     (do (print "[textures] loaded " path)
         (tset textures.cache path loaded-or-err)
         (tset textures.modtimes path (file-modtime path))
         (util.blat (util.tkeys textures.cache))
         loaded-or-err)
     (do (print "- [ERROR]: textures : couldn't load image:" path " error: " loaded-or-err)
         nil))))

(fn textures.get [path]
  (or (. textures.cache path) (load-texture path)))

(fn textures.sys-tick-start []
  (when cfg.debug
    (each [path _ (pairs textures.cache)]
      (let [known-modtime (. textures.modtimes path)
            new-modtime (file-modtime path)]
        (when (< known-modtime new-modtime)
          (tset textures.to-reload path true)
          (tset textures.modtimes path new-modtime))))))

(fn textures.sys-tick-earliest []
  (when cfg.debug
    (each [path _ (pairs textures.to-reload)]
      (when (load-texture path)
        (tset textures.to-reload path nil)))))

textures
160
transmutrix @transmutrix.bsky.social · 31/08/2026
got a really basic LDtk test going, but the library i'm using is archived, so i'm gonna write my own in Fennel this morning >:) if anybody smarter than me knows: is there a "correct" way to gracefully set a bunch of fields from one table on another table in fennel? 🤔
;; ldtk test
; i'm gonna write my own LDtk library probably lol
; but for now i'm trying out this semi-blessed, old one.
(local ldtk-world (ldtk:load "data/levels/world.ldtk"))
(ldtk:setFlipped true) ; if the layer order isn't back-to-front
(var objects [])
(var layers [])

(fn object-create [entity]
  (local result {})
  ; FIXME: could we do something more elegant here like destructuring?
  (set result.x entity.x)
  (set result.y entity.y)
  (set result.w entity.width)
  (set result.height entity.height)
  (set result.visible entity.visible)
  (table.insert objects result)
  result)

(fn objects-draw []
  (local rect love.graphics.rectangle)
  (love.graphics.setColor .8 1 .8)
  (each [_ obj (ipairs objects)]
    (rect "line" obj.x obj.y obj.w obj.h)))

(fn layers-draw []
  (each [_ layer (ipairs layers)]
    (layer:draw)))

(fn ldtk.onEntity [entity] (object-create entity))
(fn ldtk.onLayer [layer] (table.insert layers layer))
(fn ldtk.onLevelLoaded [level]
  (set objects [])
  (set layers []))

; select the level
(ldtk:goTo 1)
030
transmutrix @transmutrix.bsky.social · 29/08/2026
not a jira ticket in sight, boyo
1110
transmutrix @transmutrix.bsky.social · 26/08/2026
how appropriate that "Casino" is FIRST and "Indie Games" is FADED OUT FFS
160
transmutrix @transmutrix.bsky.social · 23/08/2026
freakin finally, man! now i have to go write a small pull request.
180
transmutrix @transmutrix.bsky.social · 22/08/2026
i took the Tiny3D quad example and slapped a sprite on it ^^
030
transmutrix @transmutrix.bsky.social · 22/08/2026
got a tiny3d example to work >:)
1210
transmutrix @transmutrix.bsky.social · 22/08/2026
>:) EHEHE
080
transmutrix @transmutrix.bsky.social · 19/08/2026
i started messing around and my MUSCLE MEMORY IS STILL THERE. i started remembering shortcut keys and stuff. holy crap dude
130
transmutrix @transmutrix.bsky.social · 19/08/2026
was talking to my wife while she plays Rocket: Robot on Wheels and remembered this ancient diorama i modeled at like 14 or something. i miss 3D modeling...
2171
transmutrix @transmutrix.bsky.social · 15/08/2026
Dust Buster is out on itch! I'll reply with a link to it if anyone would like to check it out (and actually hear the music and sounds!)
152
transmutrix @transmutrix.bsky.social · 14/08/2026
all right! the game lifecycle is in and i totally redid the HUD and stuff! a couple bits of polish left, then tomorrow it's time for sound design!
370
transmutrix @transmutrix.bsky.social · 14/08/2026
suckitude + overheating
250
transmutrix @transmutrix.bsky.social · 13/08/2026
maybe a bit excessive on the dust particles '^~^
240
transmutrix @transmutrix.bsky.social · 13/08/2026
hud + basic ball motion and brick breaking are done. paddle bonks next, then losing the ball, then dust, then maybe it'll be time for the sUCC-
120
transmutrix @transmutrix.bsky.social · 13/08/2026
some bricks and a ball but they don't do anything yet
120
transmutrix @transmutrix.bsky.social · 13/08/2026
for anyone curious, the hose code is nearly nothing lol
130
transmutrix @transmutrix.bsky.social · 13/08/2026
final nitpicks on the hose for now: i made it so the segments are allowed to be longer the farther we get from the head, for a slightly more natural curve. the movement is also a bit smoothed, but not so much it feels unresponsive. on to the brick breaker part!
110
transmutrix @transmutrix.bsky.social · 13/08/2026
it gets a bit better just by making the segments taller, and i may call this "good enough" for now '^^
150
transmutrix @transmutrix.bsky.social · 13/08/2026
super naive first attempt on the hose, sucks hahaha
150
transmutrix @transmutrix.bsky.social · 13/08/2026
vacuum head follows the cursor
130
transmutrix @transmutrix.bsky.social · 13/08/2026
nanofont3x4 set up, crappy title screen made
130
transmutrix @transmutrix.bsky.social · 13/08/2026
guess who forgot about lowresjam until the last 2 days! this goat. now i'm scrambling to do mockups and prolly don't have time to finish a game >.<
1291