devlog

Okay I did this

Although, actually I did HSL instead of HSV, I was getting which is which mixed up before :sweat_smile: The Lightness parameter only comes into play in this video to facilitate the playback cursor though, so mostly the L vs. V thing isn’t significant here—mainly this is all about Hue as I said. :slightly_smiling_face: :small_red_triangle: :green_circle: :large_blue_diamond: :m: :purple_circle: :yellow_square:

As you can see here, matching colors in this video are spaced at an octave

:eek:

I just realized something. This program would make a very nice interface for converting raw audio to MIDI or similar (i.e. auto-transcription). Almost everything needed is already there—it has a brightness parameter that would give you a very natural and easy-to-visually-parse way to set a threshold for what to call a “note”, and the program already knows how to break the sound down in terms of an arbitrary EDO (e.g. if you set the edo parameter to 12 it will spit out a piano-note-spaced representation of the sound—here the edo is set to 220 because that yields a resolution close to 2160 px/column, an entirely visual rationale in other words :stuck_out_tongue:). I could even code a “piano roll view” that would show you how the MIDI would come out in a given DAW/seqeuncer—I’d get part of the UI code for a piano roll out of that too which I’m sure would come in handy later. :smirk:

Honestly, the main things the program still needs for this are just to run in realtime+interactively (right now it just generates video frames) and some representation of MIDI data. Both can be had easily: this presents a great opportunity to break the Vulkan-and-SDL-oriented code out of the video-generating project I was showing a few weeks ago into its own library (a library that would represent a nascent game engine I might note :smirk:), and I’m pretty sure I have some code lying around somewhere for MIDI output I can reuse too (that could also go in the engine, why not). I’ve been wanting Vulkan in this program anyway because sampling and rasterizing the FFT data on the CPU is rather time-consuming and much better-suited to the GPU in performance terms.

(although the real bottleneck right now is the encoding and writing 6000 10M PNG images part, which takes hours on my system and occupies an awkward amount of hard disk space :stuck_out_tongue: I think the only way to ameliorate the slowness of that directly at this point is probably just to multithread the PNG writing code, which I should do anyway not only for this but also for the video generating stuff from earlier, that would also be a natural thing to put in the engine—anyway though if I had a realtime mode for this program I could also limit the times when I do the render-frames-to-disk stuff to the times when I really want it)

Anyway, I’ve wanted a program like this to do some form of semi-auto-transcription or guided auto-transcription or whatever for years; I often want to have transcriptions of my guitar recordings but transcribing them by hand can be very time consuming. So, I’ve imagined a program like this many times. In the past, I didn’t know enough mathematics to see clearly how to implement it, but I guess I do now :sweat_smile: in the end I kind of just walked into it accidentally I guess. Linear algebra is amazing. (this is the textbook I’ve been using if anyone else is interested, it’s really incredible stuff :eek:)

9 Likes

Haha this is great, thanks for sharing. The combo of slow acceleration and stopping on a dime is a fun way to make the platforming feel both expressively loose and stroppy. The graphics are great and very cute, especially the ending.

When I got to the part in the screenshot, hit the platform and fell down perfectly back to the first screen of level 1, I burst out laughing, that was very funny. I fearfully thought it was a game like Punishment (2005, Messhof) where each level builds on the last with a neverending threat of losing swaths of progress. I was relieved to find out that the riskiest jumps are concentrated early on.

5 Likes

Wow. Well, it’s been a wild journey with this program since my last post. As part of achieving this look I wrote code for+rendered images of a Julia set for the first time ever (…hehe:dizzy::smiley_cat::dizzy:ehehe), and I also came to a deeper understanding of how to parallelize code for optimum CPU utilization. :tiger2: Mainly for that reason, the program is much faster now, but it’s also because I picked some low-hanging fruit in the FFT+rasterization code that roughly tripled its performance prior to parallelization. Now it takes more like 20 minutes to render 6000 frames rather than hours. I still wish it was even faster :sweat_smile: but I don’t think much more of significance can be done to speed it up past here short of a whole new approach.

One thing I actually did think of in that regard is that if I did the rasterization on the GPU, in theory the GPU could be rasterizing while the host is encoding and writing the PNGs and then it would take essentially only as it long as it currently takes to write all the PNGs (since the FFTs take a relatively negligible amount of time). Synchronization would be rather complex—the GPU would probably be receiving and/or outputting data in single columns because that’s how it comes back from the FFT, but of course you probably want multiple columns of image data for a single frame of video, so triggering a PNG-writing thread on the host to start encoding a new PNG the moment the right number of columns is available from the GPU (and no sooner) would take a bit of doing. Right now the parallelization scheme I’m using is very simple (it’s the same one for both the rasterization and the PNG writing, I wrote a base class); it’s essentially just that you launch a number of threads equal to the hardware thread count or so, give each a long vector filled with “arg structs” for some thread-safe function you wrote, and have each pass all of their “arg struct” instances to the function. If I wanted to pass a sequence of image data columns from the GPU to a thread the moment they become available, I would need a more sophisticated synchronization scheme (probably involving a shared queue between threads on the host, with the main thread keeping count every time the GPU reports a finished column and submitting a batch of work to the queue when appropriate). Still, the rasterization is not the most time-consuming part of the process, the PNG encoding+writing is (it takes about twice as long), so although this would probably improve performance noticeably, it wouldn’t be like a whole new world of performance characteristics or anything.

One thing that might feel that way is if I tried using the newish Vulkan video encoding stuff, which I noticed became available on my GPU drivers a while back. That approach might be dramatically faster—the host wouldn’t really be doing anything aside from performing the FFTs, schlepping data back and forth between the GPU, and writing the video file to disk. I think I would still want the raw frame images sometimes though, so I would probably still keep the PNG-writing code around for that purpose.

Anyway, it’s fast enough right now that I can probably use it satisfactorily for my original intended purpose of making visualizations for all the music I want to put up on my new-website-in-progress. I might finish up my work on that part of the website now—I’m trying to get my new site online as fast as possible but I felt like having some sort of cool visualzations of the music was an essential feature at launch for whatever reason. It’s just fun I guess.

As a side note, the epiphany I had about threading is that spawning a thread can be expensive in both runtime and memory terms—like malloc, it’s not necessarily the greatest idea to spawn a new thread on every run through a tight inner loop. Even if you’re limiting the number of threads in flight at a time and cleaning up after finished ones promptly, the amount of time it takes to spin up a new thread can easily swamp the amount of time it takes an existing thread to do its work, limiting the amount of concurrency you can practically achieve (something I was experiencing quite vividly and frustratingly when I was experimenting with small PNGs). To maximize concurrency, it’s better to spawn a small number of threads once at the start and give each large batches of work to do.

I’m sure this is the kind of thing that’s second nature to seasoned hands at concurrent programming, but I still feel like I’m developing my chops at it and that was a deeply satisfying thing to figure out. After spending hours trying all sorts of fruitless things to improve concurrency, it was fun to suddenly recognize that (thanks to careful profiling), code accordingly, and see my CPU utlization meter finally shoot up to 100% and watch 8 PNGs being written in parallel to the ramdisk. :stuck_out_tongue: (That’s another cool thing about a ramdisk, as a side note, for anyone who read my post about them in the Linux thread a while back—you won’t run up against the overall write speed performance of e.g. a hard disk when writing out data from multiple threads at once.)

Also, now that I’m thinking about it, one feature I would really like to add from here which might not take too much work is transient detection. I’m not necessarily going to go for that right this second because I’d really like to get my new website done and I think the visualizer is already in a pretty good state as it is. That being said, I think it would be really nice to have things like my picking in the video above clearly marked, and plus it’s essential for any MIDI-generating I might ultimately want the program to do. Looking at this spectrogram from Sox of the first twenty seconds of the above audio

you can see the transients quite distinctly as ~20 ms bursts of noise in the 1.5–6.5 kHz range (which settle quickly into harmonics as these are plucked strings). These are probably getting washed out for the most part by the large window size I’m using for the FFT in the visualizer (131072, albeit with a 60 β Kaiser window applied). Maybe it would be good to also do one with a small window size, since transient detection is a context where I would rather have fine time resolution than fine frequency resolution. Anyway, stuff for the future I guess.

2 Likes

friend wants to play old romhack “Super Metroid Redesign” with their fabulous Bachelor-pixeled sprite

unfortunately, some of the data between the two patches overlaps, resulting in some rooms breaking:

i phone a friend, and with his help i’m able to hammer out a script that spits out the rom addresses for level data pointers to the overlapping section (said friend has a “room traverser” script that uses door information to find all the room headers in a hack (he’s made several hack randomizers with it)):

i expand the rom to 6 MB using the ExLoRom memory layout using Lunar Expand, copy the offending banks of the hack to the expanded section, and then write another script to make those pointers point to those new banks (subtracting 0xF4 from the highest byte)

thus, this room now loads (and the other problem rooms i ran into) but the palette is still busted:

cross-referencing the RAM map with the memory viewer, i see that the palette pointer also points to the overlapping region, so i hammer out another script to find and adjust the offending tileset-related pointers:

the offending pointers this time were for the map room palette and kraid’s tiletable

with that, this Problem Room now loads:

it still needs some more rigorous testing/validation, but it seems to Just Work :tm:

7 Likes

Been doing this pretty earnestly

6 Likes

On Sunday I challenged myself to make a game in one day, including all the sprites and music. I cheated by finishing it up today with stuff I’d done before, but don’t worry, it still feels like a game made in one day.

Estimated time to beat: 2-3 minutes

It’s like Nazo no Murasamejō + Gauntlet + Action 52 + RPG maker demo world. I doubt anyone else will dig this lol, because I leaned into making it oblique and clunky, but I’ve been having a ton of fun running it. You attack with Z. Your attack looks tiny, but you have enough of a hitbox to fight things safely. Hold X to both lock your orientation and run (shuffle a little faster).

It is wise to gather coins and fish lying around the level for extra lives. You want a life buffer because there are merciless monster generators you’ve got to clear through. If Oroki gets down to zero lives, she falls over in dismay, but you can still move and attack normally, it just doesn’t look like it. You can get back up if you find a health item. Get hit while on the ground and you’re done for good.

The objective is to destroy the monster houses, but they’re invulnerable until they’re activated. They activate all at once when you reach the last part of the level and you must fight your way back. Once you’ve purified them all, they become exit portals, so just walk inside and the level’s done. There are three music tracks composed by me:

Hope you enjoy. Have a nice day.

9 Likes

6 Likes

video post-mortem for the jam. The project lives though

3 Likes

image

image

image

image

7 Likes

unrelated to the above post: i’m starting to figure out how to work with SNES tilemaps

image

(slowly picking away at a more polished version of Tour of Italy)

the tooling situation for this kind of stuff is a trainwreck of bad programs and broken promises

4 Likes

Dipping my toe into trying out a game development engine, to see if I could create something like a Skies of Arcadia island you could talk to people in, then probably much later stretch and see if I could do a JRPG battle system.

Looking around, seems like Godot might be the best one to start with. If that ends up not working out, might try Unity.

Also puzzled out how difficult it would be to make a Ribbit King-like, but man the AI on those frogs alone would be a ball-buster. It’s funny how, for a golf game, Frolf doesn’t actually have phsyics in it.

7 Likes

I feel like getting a Frolf Like to work would be easier than getting a 3d RPG to work, speaking as someone who made a 3d game with NPCs that you talk to. You gotta have a good dialog system which can display words, have the user interact with the dialog to choose branches, have that dialog trigger stuff behind the scenes, and have a robust way to walk up to NPCs and talk to them - which is actually pretty complicated and easy for a bunch of edge cases and bugs to show up.

1 Like

If you need 3d modeling help reach out to me. I wouldn’t call myself an expert but I definitely am no longer a complete novice and I’m happy to share what helped me with blender.

1 Like

Tactical RPG




Teddy Boy, Mappy like arcade game about shoplifting (note the shelves full of unidentifiable products)
with spritesheet

Early morning dawn lighting
Screenshot-2025-05-11T15-30-00

Really small tiles for an underground game. Each screen is 160x150 (a bit bigger than the Game Boy). The tiles are 10x10 which lets them have 1px centerpoints and midlines.

12 Likes

(not actually imported into the game yet)

10 Likes

I haven’t really felt able to work on my HPL3 project for a couple of months. Starting to get back into it though, making small bits of progress. A little bit of environment is cohering. I have a layout plan, and a sketchbook to practice recording and encouraging thought about design while I can’t really work in the editor.


5 Likes

yinglets…they’re everywhere…

3 Likes

huh…

…the resemblance is entirely unintentional your honor, i swear

1 Like

image

10 Likes

SO energy