Odin + Raylib Examples

A growing gallery of experiments, demos, and engine‑building notes.

Spiral Example

package main
import rl "vendor:raylib"

sw:i32 = 800
sh:i32 = 600



draw_square :: proc() {
    x: i32 = sw/2
    y: i32 = sh/2
    w: i32 = 0
    h: i32 = 0
    offset: i32 = 20
    for i := 0; i < 20; i +=1 {
        rl.DrawRectangle(x, y, w+offset, h+offset, rl.BLUE)
        rl.DrawRectangleLines(x, y, w+offset, h+offset, rl.BLUE)
        x += offset
        rl.DrawRectangle(x, y, w+offset, h+offset, rl.RED)
        rl.DrawRectangleLines(x, y, w+offset, h+offset, rl.BLUE)
        y += offset
        x += offset
        w += offset 
    }
}




main :: proc() {

rl.InitWindow(sw, sh, "Project Dreadwitch")



for !rl.WindowShouldClose(){
    

    
    
    rl.BeginDrawing()
    
    rl.ClearBackground(rl.RAYWHITE)
    draw_square() 

    rl.EndDrawing()
    
    if rl.IsKeyPressed(.ONE){
    rl.TakeScreenshot("stairs_example.png")
}
}
    rl.CloseWindow()

}
    
Procedural Hill

// this one is specially for you Bill 

package main
import rl "vendor:raylib"
import "core:math"

MyRect :: struct {
    id:    string,
    rect:  rl.Rectangle,
    color: rl.Color,
}

Player :: struct {
    body:        rl.Rectangle,
    color:       rl.Color,
    view_radius: f32,
}

//player_controller :: proc(p: ^Player, sw, sh: f32) {
//    dt := rl.GetFrameTime()
//    speed : f32 = 250.0

//    if rl.IsKeyDown(.A) { p.body.x -= speed * dt }
//    if rl.IsKeyDown(.D) { p.body.x += speed * dt }
//    if rl.IsKeyDown(.W) { p.body.y -= speed * dt }
//    if rl.IsKeyDown(.S) { p.body.y += speed * dt }

//    p.body.x = math.clamp(p.body.x, 0, sw - p.body.width)
//    p.body.y = math.clamp(p.body.y, 0, sh - p.body.height)
//}

draw_bolt :: proc(x_pos: f32, life: f32) {
    if life <= 0 do return

        segment_height : f32 = 40.0
        current_y : f32 = -50.0
        current_x := x_pos

        // Loop until we are past the bottom of the screen
        for current_y < 1080 {
            // Horizontal jitter for THIS specific segment
            // This creates the "jagged" look
            jitter := f32(rl.GetRandomValue(-1, 1))
            current_x += jitter
            // Layer the rectangles for "pulpiness"
            for i in 0..<3 {
                current_color := rl.Fade(rl.ORANGE, life * 0.85)

                rect := rl.Rectangle{
                    current_x + jitter,
                    current_y,
                    2.0 + f32(i * 3),
                    segment_height + 5.0, // Slight overlap to prevent gaps
                }
                rl.DrawRectangleRec(rect, current_color)
            }

            // Move down for the next segment
            current_y += segment_height + 5
            // Optional: uncomment the next line to make the bolt "drift" as it goes down
            current_x += jitter
            current_x += jitter
        }
}

main :: proc() {
    //rl.SetTargetFPS(60)
    sw, sh: i32 = 1920, 1080
    rl.InitWindow(sw, sh, "Odin - Circular Vision")
    defer rl.CloseWindow()
    atmosphere := MyRect{
        id    = "Atmosphere",
        rect  = { 0, 0, f32(sw), f32(sh) },
        color = rl.Fade(rl.BLUE, 0.1),
    }

    player := Player{
        body        = { f32(sw) * 0.5, f32(sh) * 0.5, 40, 40 },
        color       = rl.GOLD,
        view_radius = 20.0,
    }

    large_chaos_strobe :: proc(p: Player) {
        cX := p.body.x + (p.body.width / 2)
        cY := p.body.y + (p.body.height / 2)

        origin := rl.Vector2{
            cX + f32(rl.GetRandomValue(-800, 800)),
            cY + f32(rl.GetRandomValue(-800, 800)),
        }

        num_layers := rl.GetRandomValue(1, 5)

        for i: i32 = 0; i < num_layers; i += 1 {
            radius := (3) + f32(i)
            rl.DrawCircleV(origin, radius, rl.Fade(rl.PURPLE, 0.08))
        }
    }

    small_chaos_strobe :: proc(pos: rl.Vector2) {
        // Use the passed-in pos instead of hardcoding 0.0
        origin := rl.Vector2{
            pos.x + f32(rl.GetRandomValue(-10.0, 10.0)),
            pos.y + f32(rl.GetRandomValue(-10.0, 10.0)),
        }

        num_layers := rl.GetRandomValue(1, 200)

        for i: i32 = 0; i < num_layers; i += 1 {
            radius := (1.0) + f32(i)
            rl.DrawCircleV(origin, radius, rl.Fade(rl.RAYWHITE, 0.005))
        }
    }

    chaos_strip :: proc() {
        cX: f32 = 0.0
        for i := 0; i < 20; i += 1 {
            small_chaos_strobe(rl.Vector2{cX, 0.0})
            cX += 150.0
        }
    }

    bolt_x: f32 = 0.0
    bolt_life: f32 = 0.0
    frame_counter := 0
    for !rl.WindowShouldClose() {
        //player_controller(&player, f32(sw), f32(sh))

        frame_counter += 1
        if frame_counter >= 100{
            rl.ClearBackground(rl.BLACK)
            frame_counter = 0
        }
        rl.BeginDrawing()

        dt := rl.GetFrameTime()
        if bolt_life <= 0 && rl.GetRandomValue(0, 100) > 98 {
            bolt_x = f32(rl.GetRandomValue(0, 1980))
            bolt_life = 1.0
        }
        if bolt_life > 0 {
            bolt_life -= dt * 5.0 // Adjust 3.0 to change how fast it vanishes
        }


        rl.DrawRectangleRec(atmosphere.rect, atmosphere.color)
        draw_bolt(bolt_x, bolt_life)

        large_chaos_strobe(player)
        chaos_strip()
        rl.DrawRectangleRec(atmosphere.rect, rl.Fade(rl.BLACK, 0.2))
        rl.DrawFPS(30,30)
        rl.EndDrawing()
        if rl.IsKeyPressed(.ONE){
        rl.TakeScreenshot("screenshot.png")
        }
    }
}

    
Procedural Hill

package main

import "core:math"
import rl "vendor:raylib"

GeoObject :: struct {
    mesh:      rl.Mesh,
    model:     rl.Model,
    position:  rl.Vector3,
    intensity: f32,
    size:      rl.Vector2,
}

CreateRidge :: proc(pos: rl.Vector3, size: rl.Vector2, peakHeight: f32, resolution: i32) -> GeoObject {
    obj: GeoObject
    obj.position = pos
    obj.intensity = peakHeight
    obj.size = size

    vCount := (resolution + 1) * (resolution + 1)
    tCount := resolution * resolution * 2

    mesh: rl.Mesh
    mesh.vertexCount = vCount
    mesh.triangleCount = tCount

    mesh.vertices = cast([^]f32)rl.MemAlloc(cast(u32)(vCount * 3 * size_of(f32)))
    mesh.indices = cast([^]u16)rl.MemAlloc(cast(u32)(tCount * 3 * size_of(u16)))

    spacingX := size.x / cast(f32)resolution
    spacingZ := size.y / cast(f32)resolution

    radiusX := size.x / 2.0
    radiusZ := size.y / 2.0

    for z in 0..=resolution {
        for x in 0..=resolution {
            i := z * (resolution + 1) + x

            localX := (cast(f32)x * spacingX) - radiusX
            localZ := (cast(f32)z * spacingZ) - radiusZ

            normX := localX / radiusX
            normZ := localZ / radiusZ
            normalizedDist := math.sqrt(normX * normX + normZ * normZ)

            targetY := -pos.y

            if normalizedDist < 1.0 {
                targetY = peakHeight * (0.5 * (1.0 + math.cos(normalizedDist * math.PI)))
            }

            mesh.vertices[i * 3 + 0] = localX
            mesh.vertices[i * 3 + 1] = targetY
            mesh.vertices[i * 3 + 2] = localZ
        }
    }

    k: i32 = 0
    for z in 0..