aaaashley's Avatar

aaaashley

@aaaa.sh.bsky.social

working with the garage door open!

92 Followers  |  324 Following  |  123 Posts  |  Joined: 19.11.2024  |  1.8464

Latest posts by aaaa.sh on Bluesky

riiiiiiiiiiiiiiiiiiiight i remember reading about this... thank you!

26.07.2025 02:57 โ€” ๐Ÿ‘ 0    ๐Ÿ” 0    ๐Ÿ’ฌ 0    ๐Ÿ“Œ 0
Video thumbnail

javascript is a great game engine, actually

25.07.2025 23:18 โ€” ๐Ÿ‘ 4    ๐Ÿ” 0    ๐Ÿ’ฌ 1    ๐Ÿ“Œ 0
the original source jsx code.

const todos = [
    { text: 'Play Donkey Kong Bananza', },
    { text: 'Continue Playing Donkey Kong Bananza', },
    { text: 'Banana', bold: true },
];

const app = <div>
  <h1 style="color: green">Todos:</h1>
  {...todos.map(todo => 
    <li style={todo.bold && "font-weight: bold"}>
      {todo.text}
      <button onclick={() => {li.remove()}}>Remove</button>
    </li>)
  }
</div>

document.getElementById('app').appendChild(app);

the original source jsx code. const todos = [ { text: 'Play Donkey Kong Bananza', }, { text: 'Continue Playing Donkey Kong Bananza', }, { text: 'Banana', bold: true }, ]; const app = <div> <h1 style="color: green">Todos:</h1> {...todos.map(todo => <li style={todo.bold && "font-weight: bold"}> {todo.text} <button onclick={() => {li.remove()}}>Remove</button> </li>) } </div> document.getElementById('app').appendChild(app);

The produced webapp. HTML that reads as the following:

Todos:
Play Donkey Kong Bananza (button) Remove
Continue Playing Donkey Kong Bananza (button) Remove
Banana (button) Remove

The produced webapp. HTML that reads as the following: Todos: Play Donkey Kong Bananza (button) Remove Continue Playing Donkey Kong Bananza (button) Remove Banana (button) Remove

The transpiled javascript code, pretty printed by the chromium debugger.

const todos = [{
    text: "Play Donkey Kong Bananza"
}, {
    text: "Continue Playing Donkey Kong Bananza"
}, {
    text: "Banana",
    bold: true
}];
const app = ( () => {
    let div = document.createElement("div");
    div.appendChild(( () => {
        let h1 = document.createElement("h1");
        h1.setAttribute("style", "color: green");
        h1.appendChild(document.createTextNode("Todos:"));
        return h1;
    }
    )());
    div.append(...todos.map( (todo) => ( () => {
        let li = document.createElement("li");
        li.setAttribute("style", todo.bold && "font-weight: bold");
        li.append(todo.text);
        li.appendChild(( () => {
            let button = document.createElement("button");
            button.addEventListener("click", () => {
                li.remove();
            }
            );
            button.appendChild(document.createTextNode("Remove"));
            return button;
        }
        )());
        return li;
    }
    )()));
    return div;
}
)();
document.getElementById("app").appendChild(app);

The transpiled javascript code, pretty printed by the chromium debugger. const todos = [{ text: "Play Donkey Kong Bananza" }, { text: "Continue Playing Donkey Kong Bananza" }, { text: "Banana", bold: true }]; const app = ( () => { let div = document.createElement("div"); div.appendChild(( () => { let h1 = document.createElement("h1"); h1.setAttribute("style", "color: green"); h1.appendChild(document.createTextNode("Todos:")); return h1; } )()); div.append(...todos.map( (todo) => ( () => { let li = document.createElement("li"); li.setAttribute("style", todo.bold && "font-weight: bold"); li.append(todo.text); li.appendChild(( () => { let button = document.createElement("button"); button.addEventListener("click", () => { li.remove(); } ); button.appendChild(document.createTextNode("Remove")); return button; } )()); return li; } )())); return div; } )(); document.getElementById("app").appendChild(app);

interesting #webdev thing today: i love JSX syntax but i don't want to bundle a runtime, so I wrote a custom parser that transpiles jsx to native DOM calls. I wrote it as a vite plugin; only 138 lines of code! no error reporting, but otherwise feature rich, and zero (really zero!) overhead.

22.07.2025 22:58 โ€” ๐Ÿ‘ 1    ๐Ÿ” 0    ๐Ÿ’ฌ 0    ๐Ÿ“Œ 0
Post image

google is experimenting on me and apparently the experiment is "how long can we serve nothing until she stops using google." not that long! i have things i want to know on the internet

19.07.2025 10:16 โ€” ๐Ÿ‘ 0    ๐Ÿ” 0    ๐Ÿ’ฌ 0    ๐Ÿ“Œ 0
Post image Post image Post image Post image

has google just stopped showing results for anyone else?

19.07.2025 10:16 โ€” ๐Ÿ‘ 0    ๐Ÿ” 0    ๐Ÿ’ฌ 1    ๐Ÿ“Œ 0
Pass nil to clear the state from the previous call, which restores a state thatโ€™s equivalent to the default values of an MTLDepthStencilDescriptor instanceโ€™s properties.

Pass nil to clear the state from the previous call, which restores a state thatโ€™s equivalent to the default values of an MTLDepthStencilDescriptor instanceโ€™s properties.

failed assertion `Set Depth Stencil State Validation depthStencilState must not be nil.

failed assertion `Set Depth Stencil State Validation depthStencilState must not be nil.

Apple docs: "yeah totally, you can pass nil. it'll just autofill the data for you. no problem dude"
Apple validation layers: "what the fuck. why would you pass nil. what is wrong with you"

05.07.2025 21:36 โ€” ๐Ÿ‘ 0    ๐Ÿ” 0    ๐Ÿ’ฌ 0    ๐Ÿ“Œ 0
Video thumbnail

here's a video version of the GIF above that doesn't seem to want to cooperate (curse you ffmpeg!!) #coding

30.06.2025 04:18 โ€” ๐Ÿ‘ 0    ๐Ÿ” 0    ๐Ÿ’ฌ 0    ๐Ÿ“Œ 0
booting the game with a URL and waiting for the script to load & run. the command:

odin run . -debug -- -r "use https://aaaa.sh/creatures/scripts/ground.hmny"

idk why this gif is so slow i swear it's not like this in the game

booting the game with a URL and waiting for the script to load & run. the command: odin run . -debug -- -r "use https://aaaa.sh/creatures/scripts/ground.hmny" idk why this gif is so slow i swear it's not like this in the game

code that starts a network request using apple's URLSession API.

platform_start_network_request :: proc(
    url: string, req: ^Platform_Network_Request,
) {
    url_path := NS.String.alloc()->initWithOdinString(url)
    nsurl := msgSend(^NS.URL, NS.URL, "URLWithString:", url_path)

    local_completion_handler := NS.Block.createLocal(
        user_data = rawptr(req),
        user_proc = transmute(proc "c" (rawptr))network_completion_handler,
    )

    url_session_task := msgSend(^URLSessionDataTask,
        msgSend(^URLSession, URLSession, "sharedSession"), 
        "dataTaskWithURL:completionHandler:", 
        nsurl,
        local_completion_handler,
    )

    msgSend(struct{}, url_session_task, "resume")
}

code that starts a network request using apple's URLSession API. platform_start_network_request :: proc( url: string, req: ^Platform_Network_Request, ) { url_path := NS.String.alloc()->initWithOdinString(url) nsurl := msgSend(^NS.URL, NS.URL, "URLWithString:", url_path) local_completion_handler := NS.Block.createLocal( user_data = rawptr(req), user_proc = transmute(proc "c" (rawptr))network_completion_handler, ) url_session_task := msgSend(^URLSessionDataTask, msgSend(^URLSession, URLSession, "sharedSession"), "dataTaskWithURL:completionHandler:", nsurl, local_completion_handler, ) msgSend(struct{}, url_session_task, "resume") }

some assembly i wrote for the scripting language stdlib. it blocks on a network request if the file URL starts with http: or https:.

some assembly i wrote for the scripting language stdlib. it blocks on a network request if the file URL starts with http: or https:.

the game's scripting language can now download files! you can import and run code, fetch html, or even read the weather. it uses a poll system & you basically have to roll your own poll loop (but there is one in the stdlib). internally, it uses the OS' networking tools (had to do some objc lol)

30.06.2025 04:16 โ€” ๐Ÿ‘ 2    ๐Ÿ” 0    ๐Ÿ’ฌ 1    ๐Ÿ“Œ 0
creatures | Memory | 725 MB

creatures | Memory | 725 MB

Creatures | Memory | 117 MB

Creatures | Memory | 117 MB

The game already ran on Apple devices via MoltenVK, so this was mostly a learning exercise. There are some benefits though. Avoiding MoltenVK reduces code size, and improves CPU & GPU performance as we get closer to the hardware. Most importantly, it saves a TON of memory.

Vulkan / MoltenVK | Metal

26.06.2025 08:50 โ€” ๐Ÿ‘ 1    ๐Ÿ” 0    ๐Ÿ’ฌ 0    ๐Ÿ“Œ 0
vertex_buffer = gpu_buf_writeable(Block_Vertex, MAX_BLOCK_VERTS, .Vertex)
instance_buffer = gpu_buf_writeable(Boro_Instance, MAX_BOROS, .Vertex)
boro_chunks_texture = gpu_img({CHUNKS_PER_BORO, MAX_BOROS, 1}, .R32_UInt, 
    {.Sampled}, {.CPU_Writeable})
energy_texture = gpu_img({2048, 2048, 16}, .R8_UInt, 
    {.Sampled}, {.CPU_Writeable})
samp = gpu_sampler()

block_drawing_resource_arena = gpu_initialize_resources(
    &vertex_buffer.untyped, &instance_buffer.untyped, 
    &boro_chunks_texture, &energy_texture)

chunks_binding_set = gpu_binding_set(
     { .Fragment }, &chunks_binding_set_layout, 
     { &boro_chunks_texture, &energy_texture, &samp },
)

block_vert_shader := gpu_shader("block_vert")
block_frag_shader := gpu_shader("block_frag")

block_render_layout = gpu_render_layout(
    color_attachments = { 
        { format = gpu_surface_format(), load_op = .Clear,
          transition = .To_Surface, 
        },
    },
    depth_attachment = GPU_Attachment_Description { 
        format = depth_image.format, load_op = .Clear,
    },
)

block_drawing_pipeline = gpu_graphics_pipeline_with_layout(
    &block_render_layout, &blocks_binding_layout,
    block_vert_shader, block_frag_shader,
    { gpu_vertex_desc(Block_Vertex), gpu_instance_desc(Boro_Instance) },
    depth_enabled = true,
)

vertex_buffer = gpu_buf_writeable(Block_Vertex, MAX_BLOCK_VERTS, .Vertex) instance_buffer = gpu_buf_writeable(Boro_Instance, MAX_BOROS, .Vertex) boro_chunks_texture = gpu_img({CHUNKS_PER_BORO, MAX_BOROS, 1}, .R32_UInt, {.Sampled}, {.CPU_Writeable}) energy_texture = gpu_img({2048, 2048, 16}, .R8_UInt, {.Sampled}, {.CPU_Writeable}) samp = gpu_sampler() block_drawing_resource_arena = gpu_initialize_resources( &vertex_buffer.untyped, &instance_buffer.untyped, &boro_chunks_texture, &energy_texture) chunks_binding_set = gpu_binding_set( { .Fragment }, &chunks_binding_set_layout, { &boro_chunks_texture, &energy_texture, &samp }, ) block_vert_shader := gpu_shader("block_vert") block_frag_shader := gpu_shader("block_frag") block_render_layout = gpu_render_layout( color_attachments = { { format = gpu_surface_format(), load_op = .Clear, transition = .To_Surface, }, }, depth_attachment = GPU_Attachment_Description { format = depth_image.format, load_op = .Clear, }, ) block_drawing_pipeline = gpu_graphics_pipeline_with_layout( &block_render_layout, &blocks_binding_layout, block_vert_shader, block_frag_shader, { gpu_vertex_desc(Block_Vertex), gpu_instance_desc(Boro_Instance) }, depth_enabled = true, )

The rendering code is simple too: the API is built on my personal mental model of how a GPU works, with some hacks and caveats for the idiosyncrasies of specific backends.

here's a code sample so you can see how simple it is. virtually no overhead!!

26.06.2025 08:50 โ€” ๐Ÿ‘ 1    ๐Ÿ” 0    ๐Ÿ’ฌ 1    ๐Ÿ“Œ 0
Video thumbnail

Wrote a custom cross-platform GPU API for my game's engine, so now it can directly speak both Metal and Vulkan through the same rendering code. The new API is designed to be super low-overhead: it doesn't allocate memory or do any hidden work. Here's a (slightly boring...) demo! #gamedev #techart

26.06.2025 08:50 โ€” ๐Ÿ‘ 8    ๐Ÿ” 1    ๐Ÿ’ฌ 1    ๐Ÿ“Œ 0
Video thumbnail

After 9 months, I'm cutting the raytracing-based voxel renderer for my game and going back to basic meshing. (I'll have to do a post-mortem on why raytracing was a horrible idea!!) Anyway, here's a visualization for some basic voxel meshing stuff #gamedev

13.06.2025 06:37 โ€” ๐Ÿ‘ 9    ๐Ÿ” 1    ๐Ÿ’ฌ 1    ๐Ÿ“Œ 0
Heroes and Villains (Stereo)
YouTube video by The Beach Boys - Topic Heroes and Villains (Stereo)

www.youtube.com/watch?v=v3GP...

11.06.2025 17:37 โ€” ๐Ÿ‘ 0    ๐Ÿ” 0    ๐Ÿ’ฌ 0    ๐Ÿ“Œ 0
Post image

they'll do anything before they bring back skeuomorphism

10.06.2025 00:22 โ€” ๐Ÿ‘ 0    ๐Ÿ” 0    ๐Ÿ’ฌ 0    ๐Ÿ“Œ 0
A git diff, with a comment saying "Chunks are 4x4x4, Boros are 32x32x32. These values are chosen carefully and woven into the game's code and algorithms. These are not for playing with." As is seen in the diff, the comment and the corresponding code has been played with.

A git diff, with a comment saying "Chunks are 4x4x4, Boros are 32x32x32. These values are chosen carefully and woven into the game's code and algorithms. These are not for playing with." As is seen in the diff, the comment and the corresponding code has been played with.

me when i ignore my own comment

01.06.2025 21:02 โ€” ๐Ÿ‘ 1    ๐Ÿ” 0    ๐Ÿ’ฌ 0    ๐Ÿ“Œ 0

he's a dog, and he plays basketball. so simple, and yet so deeply complex

16.05.2025 08:15 โ€” ๐Ÿ‘ 0    ๐Ÿ” 0    ๐Ÿ’ฌ 0    ๐Ÿ“Œ 0
Video thumbnail

quick note: i've put a bunch of my game development screenshots & videos at aaaa.sh/creatures in case people want to take a look at what i've been working on without scrolling through here #gamedev

14.05.2025 05:27 โ€” ๐Ÿ‘ 3    ๐Ÿ” 1    ๐Ÿ’ฌ 0    ๐Ÿ“Œ 0
In a badly designed book, the letters mill and stand like starving horses in a field. In a book designed by rote, they sit like stale bread and mutton on the page. In a well-made book, where designer, compositor and printer have all done their jobs, no matter how many thousands of lines and pages, the letters are live. They dance in their seats. Sometimes they rise and dance in the margins and aisles.

In a badly designed book, the letters mill and stand like starving horses in a field. In a book designed by rote, they sit like stale bread and mutton on the page. In a well-made book, where designer, compositor and printer have all done their jobs, no matter how many thousands of lines and pages, the letters are live. They dance in their seats. Sometimes they rise and dance in the margins and aisles.

The word "form" can be surgically revised, instead of rewritten, to become the word "farm" or "firm" or "fort" or "fork" or "form", or with a little more trouble, to become the word "pineapple."

The word "form" can be surgically revised, instead of rewritten, to become the word "farm" or "firm" or "fort" or "fork" or "form", or with a little more trouble, to become the word "pineapple."

Narrow row houses flush with the street are found not only in urban slums but in the loveliest of the old Italian hill towns and Mediterranean villages. A page full of letters presents the same possibilities. It can lapse into a typographic slum, or grow into a model of architectural grace, skilled engineering and simple economy. Broad suburban lawns and wide typographical front yards can also be uninspiringly empty or welcoming and graceful. They can display real treasure, including the treasure of empty space, or they can be filled with souvenirs of wishful thinking. Neoclassical birdbaths and effigies of liveried slaves, stabled boys and faded pink flamingoes all have counterparts in the typographic world.

Narrow row houses flush with the street are found not only in urban slums but in the loveliest of the old Italian hill towns and Mediterranean villages. A page full of letters presents the same possibilities. It can lapse into a typographic slum, or grow into a model of architectural grace, skilled engineering and simple economy. Broad suburban lawns and wide typographical front yards can also be uninspiringly empty or welcoming and graceful. They can display real treasure, including the treasure of empty space, or they can be filled with souvenirs of wishful thinking. Neoclassical birdbaths and effigies of liveried slaves, stabled boys and faded pink flamingoes all have counterparts in the typographic world.

A comparison of different page sizes with musical intervals.

A comparison of different page sizes with musical intervals.

just finished reading "The Elements of Typographic Style" by Robert Bringhurst; it was one of the most well-written textbooks I've ever read! here are some of my favorite excerpts #design #typography

12.05.2025 10:06 โ€” ๐Ÿ‘ 2    ๐Ÿ” 0    ๐Ÿ’ฌ 0    ๐Ÿ“Œ 0
Post image

man i really went from "yeah i think it would be fun to make video games" to "HOLY SHIT WHAT IS THE DIFFERENCE BETWEEN X PRIME AND X PRIME PRIME" real quick #gamedev

01.05.2025 19:25 โ€” ๐Ÿ‘ 4    ๐Ÿ” 1    ๐Ÿ’ฌ 1    ๐Ÿ“Œ 0

also please note that calculating AABB-style boxes around each object is expensive in and of itself.

20.04.2025 06:11 โ€” ๐Ÿ‘ 0    ๐Ÿ” 0    ๐Ÿ’ฌ 0    ๐Ÿ“Œ 0

I don't have a good solution for this yet. I think the ideal solution involves:
- Using marching cubes to conform a slightly high-poly mesh outside the object.
- Using mesh deformation to destroy it as much as possible while staying outside the inner voxel data.
But wanted to share. I am tired.

20.04.2025 06:02 โ€” ๐Ÿ‘ 0    ๐Ÿ” 0    ๐Ÿ’ฌ 0    ๐Ÿ“Œ 0
several voxel objects with well-constrained low-poly meshes. a stick figure thinking "wow these meshes are well fit to the voxel obejcts that they belong to!"

several voxel objects with well-constrained low-poly meshes. a stick figure thinking "wow these meshes are well fit to the voxel obejcts that they belong to!"

So the obvious move is to shrink the render boxes. But how do we find the right new shape? How can we take voxel data and constrain a low-poly mesh to it in an efficient way? The simplest way is to find the smallest AABB-style box, but that excludes shapes like slopes and arcs. (5/N) #gamedev

20.04.2025 06:02 โ€” ๐Ÿ‘ 4    ๐Ÿ” 1    ๐Ÿ’ฌ 2    ๐Ÿ“Œ 0
a very messy render box with 5 objects in it. text, which reads "each one of these objects has their own render box that is individually ray-traced in shader code." a stick figure is looking at it thinking "uh oh."

a very messy render box with 5 objects in it. text, which reads "each one of these objects has their own render box that is individually ray-traced in shader code." a stick figure is looking at it thinking "uh oh."

When we spawn multiple objects, each one gets its own mesh. The is fine on its own, the problem is that the minimum mesh size is 32x32x32, so we end up raytracing shaders all over empty space for smaller objects. Many pixels get ray-traced N times, where N is the number of objects. (4/N) #gamedev

20.04.2025 06:02 โ€” ๐Ÿ‘ 4    ๐Ÿ” 1    ๐Ÿ’ฌ 1    ๐Ÿ“Œ 0
Post image a drawing, a box labeled "REAL MESH (SENT TO GPU") around a big black box that encases a brown blob of blocks labeled "THIS IS THE SHAPE WE RENDER AND DRAW." Someone standing outside of the box labeled "view ray in rasterizer".  They are sending out a ray from their eyes that turns green inside of the black box, the ray is labeled "view ray simulated in shader."

a drawing, a box labeled "REAL MESH (SENT TO GPU") around a big black box that encases a brown blob of blocks labeled "THIS IS THE SHAPE WE RENDER AND DRAW." Someone standing outside of the box labeled "view ray in rasterizer". They are sending out a ray from their eyes that turns green inside of the black box, the ray is labeled "view ray simulated in shader."

this is because each object is actually rendered as a cube. DDA is performed to ray trace 'inside' of the cube, which is how we get the actual shapes. here's a diagram and a debug visual for illustration. (3/N) #gamedev

20.04.2025 06:02 โ€” ๐Ÿ‘ 4    ๐Ÿ” 1    ๐Ÿ’ฌ 1    ๐Ÿ“Œ 0
Post image

here's profiling capture for three frames, the middle one is a complete game tick. you can see that green bit in the middle, which is object creation. the tan stuff to the left (labeled '163') is collision code, and the black bars *everywhere* is voxel rendering. (2/N) #gamedev

20.04.2025 06:02 โ€” ๐Ÿ‘ 3    ๐Ÿ” 1    ๐Ÿ’ฌ 1    ๐Ÿ“Œ 0
Video thumbnail

found a problem with my game today :) i wanted to stress-test the collision detection & gravity routines by spawning a ton of objects, turns out the bottleneck is rendering! each object caries with it a huge amount of rendering overhead, so the game slows to a crawl... (-ฬฅฬฅฬฅฬฅฬฅฬฅฬฅฬฅฬฅฬฅฬฅฬฅฬฅฬฅฬฅฬฅฬฅฬฅฬฅฬฅฬฅฬฅฬฅฬฅฬฅแท„_-ฬฅฬฅฬฅฬฅฬฅฬฅฬฅฬฅฬฅฬฅฬฅฬฅฬฅฬฅฬฅฬฅฬฅฬฅฬฅฬฅฬฅฬฅฬฅฬฅฬฅแท… ) (1/N) #gamedev

20.04.2025 06:02 โ€” ๐Ÿ‘ 4    ๐Ÿ” 1    ๐Ÿ’ฌ 1    ๐Ÿ“Œ 0
Video thumbnail

i often find myself writing tiny visualizers for chunk metadata, so i added a really simple debug menu that gives me all the info i need. it's also a good test of keyboard navigation support for my immgui system. #gamedev

14.04.2025 01:06 โ€” ๐Ÿ‘ 7    ๐Ÿ” 1    ๐Ÿ’ฌ 0    ๐Ÿ“Œ 0
Post image 10.04.2025 16:55 โ€” ๐Ÿ‘ 1    ๐Ÿ” 0    ๐Ÿ’ฌ 0    ๐Ÿ“Œ 0
Video thumbnail

objects have gravity! movement is based on the center of mass, which has to be updated as objects are modified. now it's time for collision detection... ( โš† _ โš† ) #gamedev

08.04.2025 17:10 โ€” ๐Ÿ‘ 10    ๐Ÿ” 0    ๐Ÿ’ฌ 0    ๐Ÿ“Œ 0

ah ok, yeah, rand seems like it was made for cryptography and related fields. if you get around to swapping rand out, fastrand is *much* better for games. my understanding is that the bevy_rand crate is partially based on fastrand as well.

08.04.2025 00:35 โ€” ๐Ÿ‘ 0    ๐Ÿ” 0    ๐Ÿ’ฌ 1    ๐Ÿ“Œ 0

@aaaa.sh is following 19 prominent accounts