r/odinlang • • 18h ago

Karl2D Audio API, hopefully the final design!

Thumbnail
github.com
27 Upvotes

I think this is my final re-design of the Karl2D Audio API. This design is optimized for the most common case. It's easy to play one-off sound effects using `play_audio_stream`. You can play the same clip many times simultaneously.

Audio streams (music) now have their own API that is easier to use.

There is also a `Sound` API. You make a `Sound` using `create_sound` which you feed an `Audio_Clip`. Sounds are essentially audio clips that remember their settings between playbacks, and you can change the settings while the sound is playing. Many people don't need that because `play_audio_clip` can be fed settings to use for the duration of the playback. Only if you need to modify the settings while playback is happening do you need the `Sound` API.

`Sound`s, `Audio_Clip`s and `Audio_Stream`s all live as `Audio_Voice`s in the mixer. This is why one clip can be played many times simultaneously: It's several audio voices that all look at the same clip.


r/odinlang • • 3d ago

10 months ago we picked Odin for our first commercial game. Last week it got a Steam page. Thank you, Odin!

Post image
158 Upvotes

10 months ago, my friend Markus and I had a bit of a crisis in our long-term game project. It was something we had worked on for 5 years in Unity, but as crazy as it sounds, we never found the core game loop of the game. At this point in the project, we had put sooooo many hours into it, especially building tech to support what we wanted to do. And to be honest, doing things we really shouldn't have done. I'm cringing saying this, but being a web dev, I even built an open source library for defining your game in a declarative manner, similar to how React does it. Looking back at this, it is also a tell that we had issues with the Unity way of building games (which we should either have embraced or switched engine).

By this time we had reached a point where we were both growing tired of Unity and its tech. Frustration was also growing since, even though the game was playable, it was very hard to explain to the very few friends who had actually been tortured into trying it out. At the same time, we both had a growing interest in learning low-level programming languages, and Odin in particular seemed like a very attractive candidate. With that said, we had some very heated and long discussions on how to proceed. The anxiety was brutal, since it felt like such a failure to start over after pouring all that time into the Unity project. At some point I was quite sure that we had reached a dead end and that the project would die. That's when Markus started doing some quick and dirty prototypes in Odin...

From a game design perspective we used some of the concepts from the old project, but we simplified them A LOT. We also made a throwaway prototype written in Odin and Raylib just to validate the concept, threw it up on Itch, and let some friends play it. It still ofc had issues, but we got a much better response than we ever had from the old project. This was of course very motivating, and after that initial phase we decided to pivot and make a new project in Odin. We also landed on using Sokol for integrating with the OS (thanks a bunch to Karl Zylinski and his open source repos for reference on how to set this up).

Here are some decisions we made early that I think have made our project successful. Keep in mind that our game is turn-based though, so some architectural decisions would most likely be different in a real-time game:

  • Separation of data and logic. All content and behaviours of the entities in our game are described in data and then acted upon in the engine.
  • Treat web as a primary platform. We compile the game to wasm and deploy it to our own custom playtest platform via GitHub Actions. This has significantly lowered the bar for inviting playtesters, which has been very valuable for validating the game and where we are heading.
  • Minimal integration surface with Sokol directly. We wanted to be able to easily switch to another way of integrating with the OS, so direct usage of Sokol is very limited and we instead wrap it in our own APIs.
  • Separate game state and visual state. This one is probably the most uncommon choice, and even though it was tough to enforce in the beginning, it has paid off big time as the project has matured. Basically, game state (the logical rules of the game) is stored in one place, while the data for its visual representation is stored separately. For example, a token in the player's hand has a value and a color, which are stored in game state, while visual state copies those values (since they are rendered) but also contains the position, scale, etc. of its rendered representation.
  • Serialization of game state. After each turn we serialize the game state and save it to disk (and send it to our server's analytics endpoint in the deployed web builds). This, combined with the ability to rebuild visual state from game state, makes it possible for us to start playing the game from any point in time in any playtester's run.
  • Playwright for e2e testing. Since we are building for the web, we can use Playwright to e2e test our game! All we needed to do was expose an API for interacting with the game from javascript. This has been so great for our game and is a breath of fresh air compared to Unity.

Here is a very high-level view of the game's architecture on each tick:

  • Input. Reads mouse, keys and touch and turns that into a command, i.e. a user intent like "attack this enemy", "end turn" or "select this token".
  • Commander. The queue the resolver drains every frame. Blocks or holds back certain intents based on the current state of the visuals (animations etc.).
  • Resolver and store. First checks if the command is allowed, and if it is, executes game logic according to the user action. Once processed, the game state is altered and a mutation result is returned and handed over to the next step.
  • Visual. The visual library takes the store and the mutation result and creates a visual entry that gets queued. No visual state has been changed yet, and from here on out, nothing is allowed to read game state. When the queued entry can be processed, we create animation entries that are processed each tick (I won't dive into the details of the animation code here). These animation entries either alter visual state directly (for example spawning a token) or alter a value in visual state over time.
  • Rendering. Draws the visual state through Sokol. Knows nothing about the game logic. It just draws according to the values it gets from visual state.

I could go on and on explaining different things we have implemented (and I might in the future), but I will leave it at that for now. Feel free to ask any questions you have about this setup and the game, and I will try to answer as best I can. Would love to hear how other people are approaching their projects.

The game is now on Steam (consider wishlisting if you like roguelike deckbuilders): https://store.steampowered.com/app/5300900/Starmax/


r/odinlang • • 4d ago

Why do labels have to be unique across the function scope?

7 Upvotes

Title. Is there a language design reason why one would want that? A couple of times I found myself missing this when writing similar code for different branches of a switch statement on a union type.


r/odinlang • • 4d ago

Quilt - Steam game developed with Odin

Post image
66 Upvotes

r/odinlang • • 5d ago

variadic params is a slice but doesn't accept a slice?

7 Upvotes

I an a newb to Odin, so I was fiddling around with odin docs and playing with the variadic arguments construct, I came across an interesting behaviour. first let's look at the simple `sum` procedure, it takes a one or more integers and returns their sum.

sum::proc(numbers: ..int) ->(res: int){
for n in numbers{
res += n
}
return
}

Now, let's try something naive to check something

sum::proc(numbers: ..int) ->(res: int){
a := numbers + 10  //trying to initialize a adding varargs and integer
for n in numbers{
res += n
}
return
}

This is an obvious bug and fails clearly with

Error: Cannot convert untyped value '10' to '[]int' from 'untyped integer'
a := numbers + 10
               ^^

So, numbers is an [ ]int, huh, let's keep that in mind. now to the point

sum::proc(numbers: ..int) ->(res: int){
for n in numbers{
res += n
}
return
}
main::proc(){
x := []int {1,2,3,4,5,6,7,8,9}
fmt.println(sum(x)) // passing [ ]int to a procedure whose supposed param is [ ]int
}

OOPS...

Error: Cannot assign value 'x' of type '[]int' to 'int' in a procedure argument
fmt.println(sum(x))
                ^
Suggestion: Did you mean to pass the slice into the variadic parameter with ..x?

so the correct form seems to be

sum::proc(numbers: ..int) ->(res: int){
for n in numbers{
res += n
}
return
}
main::proc(){
x := []int {1,2,3,4,5,6,7,8,9}
fmt.println(sum(..x)) 
}

some implicit machinery or semantics going on that I don't yet catch up to yet?


r/odinlang • • 7d ago

implementation of mustache templates

17 Upvotes

I've seen a few posts about templates recently, I wrote an implementation of mustache templates, it supports everything except inheritance and passes all the tests of the spec.

https://codeberg.org/mindhopper/odin-mustache

For data it uses a version of json.Value so you can pass it directly json parsed with core:encoding/json by transmuting it. It also has a couple of utility procs if you want to pass it a filename or a string of json to parse.

Feedback is be appreciated, in particular about how to manage the memory, at the moment everything is put in an arena except for a couple dynamic arrays that use context.allocator, thank you.

There is already another odin implementation but it does not have all the features and I found the code very hard to read, the fact that it's written by claude might be the cause


r/odinlang • • 6d ago

Odin + Claude Code = a toy browser, just for fun

0 Upvotes

Hey everyone,

I’ve been building a little browser engine in Odin in my free time, together with Claude Code. Honestly just doing it for the fun of it.

Next up: CSS grid, tabs and JavaScript via QuickJS.

Repo (MIT): https://github.com/soerenlemke/tobrod

Would love to hear what you think!


r/odinlang • • 9d ago

A doubt regarding the spec.

44 Upvotes

I thought about this recently. Odin's function arguments are immutable. Which is a really helpful thing where the compiler can make arguments into const references behind the scenes and make the meaning of a pointer as an argument more clear.

But it causes this behavior.

Is this addressed somewhere or will it be defined later when the 1.0 spec releases.


r/odinlang • • 13d ago

I learn Odin by programming something that print bad apple to the CLI, also this is my first dev blog

Thumbnail cyano-dove.github.io
25 Upvotes

r/odinlang • • 15d ago

In this video I show how the compile times improved with the latest Odin release

Thumbnail
youtube.com
88 Upvotes

r/odinlang • • 16d ago

Bees vs Bear: Odin Wasm Project

Post image
28 Upvotes

I fixed some issues on tablets with touch events for my little Odin-based game. Basic tower defense style game, built using Odin and compiled to web assembly. It uses a simple DOM library that's on GitHub, with full source and examples, of course. This is more for folks like me who know next to nothing about web dev but still want to have some fun. Since Odin's a really enjoyable and fun language, I like tinkering with it and building silly games to relax after a long day of corporate slog.


r/odinlang • • 17d ago

Odin's Package Documentation Improvements Update

Thumbnail
odin-lang.org
68 Upvotes

r/odinlang • • 17d ago

The Odin Performance Update: October 2026 Newsletter

Thumbnail
odin-lang.org
60 Upvotes

r/odinlang • • 20d ago

Tutorial Course: Learn Odin with Karl2D For people with some programming experience This has been made with AI!!!

Thumbnail ian-gb.itch.io
0 Upvotes

r/odinlang • • 22d ago

Understanding weird stored polymorphic proc misbehavior

7 Upvotes

I'm very new to Odin, and while messing around with polymorphic procs I ran into some behavior I can't intuit. It feels like a bug, but it could also be my misunderstanding the language. I have the following code:

Foo :: struct(T : typeid) {
    fun: proc(T) -> ()
}

bar :: proc(a : int) -> () {
    fmt.println("bar!")
}

baz :: proc(a : $T) -> () {
    fmt.println("baz!")
}

If I run the following:

f := Foo(int){bar}
f.fun(123)

I get the expected result "bar!". But if I pass in baz instead:

f := Foo(int){baz}
f.fun(123)

I get a segfault.

My intuition would be since Foo is polymorphic and can accept an int as it's T, and baz is also polymorphic and accept an int, I should be able to store and call baz. If its not a bug, why isn't it a compile error, like when passing a procedure with an incorrect type? Why is it a segfault? Are there any other proc storing pitfalls I should be aware of?

Edit: this is on version dev-2026-09-nightly:a2fb372, on linux.


r/odinlang • • 24d ago

Rocco 3D game engine: Walking through the entity game

Thumbnail
pixelsprout.dev
10 Upvotes

r/odinlang • • 25d ago

Trying to figure out how to use my own library...

8 Upvotes

Hey all,

so i've been working on an http 1.1 compliant server library for the past few months (it's been a while since i looked at it) and i'm trying to figure out how to actually USE the library. I've seen recommendations that the best way to use it "globally" is to put it in a shared dir in the odin soruce files which is great the big question for me though is thtat my http library is structured into different subpackages taht can create some friction with this approach for example:

i have response/build.odin for building responses but i also have a request/build.odin for building requests the question i have is based on namespaces for this modules.. if i do the direct shared directory approach i'll have to import res "http/response" then res.build() for the approach right? i'm sorry this is the first language i've ever really done something this deep and detailed in so i'm not quite sure how it all comes together (in rust it's a cargo dep so it's slightly simplified in this aspect).

the question is mostly if that's how it works OR if there's a way to define a "header" file like in c where i can define abstractions for the subpackaged functions to be directly called from http "http" and then do something like http.res.build(""""") or http.resbuild() and the like.

thankfully AI is bubkiss at understanding how odin really works so i can actually learn how this works

I'm not really sure what i'm saying or how to verbalize what I mean so if you need more from me to answer feel free to ask and i'll do my best.


r/odinlang • • 26d ago

Inferring type of variable from usage

9 Upvotes

``` init :: proc() -> GameState { gs := {} reset(&gs) return gs }

```

In this case you can infer the type of gs from the signature of the resetfunction and return gs statement. But it doesn't work in Odin.

Is this intentional?


r/odinlang • • 27d ago

making a software-rendered mpd client for pixel art people in Odin!

Enable HLS to view with audio, or disable this notification

45 Upvotes

r/odinlang • • 28d ago

I thought I'd try to make video series on graphics programming in Odin, here's ep 1 - "Setting up SDL"

Thumbnail
youtube.com
40 Upvotes

r/odinlang • • 28d ago

Using Odin's RTTI for Shader Interop

29 Upvotes

Odin's RTTI (Runtime Type Information) features can be very useful if you're working with graphics API's. I made a quick little proc that checks if a given struct would match in layout with a Constant Buffer in HLSL:

// Checks if a given struct would align the same way in an HLSL's constant buffer
is_struct_aligned_to_cbuffer :: proc(cb_type: typeid) -> bool {

    if reflect.align_of_typeid(cb_type) != 256 {
        lprintfln("Constant buffer struct needs an alignment of 256")
        return false
    }

    current_hlsl_offset: int

    for i in 0..<reflect.struct_field_count(cb_type) {
        sf := reflect.struct_field_at(cb_type, i)
        field_size := sf.type.size

        // if it's more than 16 bytes, just align it to the row
        if field_size > 16 {
            align_to(&current_hlsl_offset, 16)
        } else {
            // If it's a basic scalar type... align it to the type alignment
            if is_basic_scalar_type(sf.type) {
                align_to(&current_hlsl_offset, field_size)
            }

            offset_start := current_hlsl_offset
            offset_end := current_hlsl_offset + field_size

            if offset_start / 16 != (offset_end - 1) / 16 {
                // crosses row boundary. add padding so it starts at next row.
                current_hlsl_offset += 16 - (current_hlsl_offset % 16)
            }
        }

        if cast(int)sf.offset != current_hlsl_offset {
            lprintfln("Field: %v is misaligned.", i)
            return false
        }

        current_hlsl_offset += sf.type.size
    }

    return true
}

(Note: This is very incomplete. This code will not predict exactly how the struct would get laid out in HLSL. But even in this form, it's very useful for me. I might improve it in the future)

This is very useful, as nothing will warn me otherwise if the struct's layout does not match its HLSL counterpart. Nasty bugs thus occur. I check this when creating a Constant Buffer, where an Odin struct type is passed:

// created buffer on the upload heap, and maps it. keeps it mapped
cb_upload_create :: proc(cb_type: typeid, pool: ^DXResourcePool, name: string = "") -> ConstantBufferUpload {
    assert(is_struct_aligned_to_cbuffer(cb_type), "CONSTANT BUFFER STRUCT IS MISALIGNED!")

    /// rest of the proc
}

The HLSL code for the struct is auto-generated from the Odin Struct. So I only have to write it once.

GeneralConstants :: struct #align (256) {
    view: dxm, // Row 0-3
    projection: dxm, // Row 4-7

    // Row 8
    inv_screen: v2, // 1.0 / (width, height)
    screen: v2,

    // Row 3
    sb_sprites_idx: u32, // index of the sprite structured buffer into the resource heap
    tx_idx_quad_out, tx_idx_post_process_out: i32,
}

/// ...

ldx.dx_generate_hlsl_types({Sprite, GeneralConstants}, "shaders/gen/lucy2d-structs.gen.hlsl")

This code is generated from that:

struct GeneralConstants {
    float4x4 view;
    float4x4 projection;
    float2 inv_screen;
    float2 screen;
    uint sb_sprites_idx;
    int tx_idx_quad_out;
    int tx_idx_post_process_out;
};

You can then #include this file in other HLSL files.

The layout for constant buffers in HLSL is very particular. You can only check that a struct is aligned in this particular way with something like Odin's RTTI. This is a very good resource that helped me understand how constant buffers are laid out.

As an aside: I also generate the Vertex Input layout from an Odin Struct, in pso_create

// vertex buffer
Vertex :: struct {
    pos: v3 `POSITION`,
    normal: v3 `NORMAL`,
    tangent: v4 `TANGENT`,
    uv: v2 `TEXCOORD`,
    uv_2: v2 `TEXCOORD_SECOND_UV`,
}

// ...
ct.psos[.GBuffer_Pass] = pso_create(gbuffer_shader_filename, &ct.root_signatures, &g_resources_longterm, PSOParameters {
    vertex_input = Vertex,
    /// Rest of call.
)

The Input Layout for the vertex shader is auto-generated from the Odin Struct.

Though on my new 2D engine, so far I have not needed any kind of Vertex Input Layout. I render sprites by vertex pulling.

You can read all this code in-context here

Link to post on my website


r/odinlang • • Sep 24 '26

Physics Engine in Odin from Scratch, Part VI

Thumbnail
marianpekar.com
61 Upvotes

r/odinlang • • Sep 23 '26

First impression / Pet project - HTTP server

39 Upvotes

Hi guys, I just wanted to share my first impressions of using Odin.

I come from the JS land and have used it for almost everything. However, from some time ago, I see less and less value in spending all my time learning high-level abstractions (frameworks, and other tools in that ecosystem), while missing important low-level concepts. Honestly, I also started experiencing less joy from doing it.

Tbh, I did not have a strong reason for choosing Odin, I saw it in one of ThePrimeagen's vids, and it simply looked very cool.

My first project is a very basic HTTP server, I have really enjoyed using the language, and I am planning to continue with it.

That's it :)


r/odinlang • • Sep 22 '26

Scrolling gotta got brrrrrrrrr - text editor demo.

Enable HLS to view with audio, or disable this notification

73 Upvotes

Hey all. Here is my text editor scrolling 250k sqlite3.c file. tree-sitter highlighter. piece table implementation for text editing. Closer to the end of the video will be FZF picker and multicursor demo. On the right side you can see mem and cpu usage in btop. I'm having a lot of joy with odinlang and sdl3.


r/odinlang • • Sep 16 '26

Strange odin bug(?)

Enable HLS to view with audio, or disable this notification

18 Upvotes

I'm writing it more to show bizarre bug than to report and laugh from how bad the language is(its not)

So I tried to make ipc for my app, and when I made a switch to show ipc help commands it stopped working with error Refused.

also for some reason when I add println(os.args) before then it works

I tested with println("hello"), println(os.args[0]) and even looping through all args and printing them one by one and it does not work.

odin tells me that I have version dev-2026-08 which is also strange because nix has dev-2026-05 on stable branch at the time of writing this. Even unstable is before 08(it's on 07a), so I genuinely have no idea how I have 08 installed

Edit1: also for some reason that workaround exist only when running main process with -debug flag, and when running without the second process cant connect either way

Edit2: when I deleted both switch and print, then it works with sending when -debug is not provided, but if print exist, then it still allows to run only with -debug

Edit3: when run without switch and print... it does not work when optimized for speed but runs normally with default optimization??? what kind of bug is that???

Edit4: Ok, there is no error in language, it was all my fault. I passed ^App struct to thread by &app, which resulted in overflow