What’s everybody working on? (August Edition)

For about 20 years I’ve had this idea for a multi-site CMS app. I’ve settled on Zig as the implementation language. It’s a monster of a project though, and right now I’m trying to backtrack on about half the stuff I’ve done in the past week because it is ugly. First Zig project.

Secondly, I found I didn’t like how documentation was being generated through Auto Doc, and I got sidetracked to basically reimplement it my own way. I’ll publish that soon as I think some people will really find it useful.

3 Likes

Still tinkering with my toy language “Moin” in my free time.

Because I like creating simple games and to see things moving, I am working on integrating raylib (the Zig wrapper). So it will be a Moin wrapper for the Zig wrapper for the C library :smiley:.

As a first step, I created helper functions to unwrap the function args from Moin Values to Zig structs/i32/[]const u8 etc.

Next step will be to auto-generate the wrappers if possible (similar to what SWIG does for several languages).

Not worth showing yet.

2 Likes

I’ve been continuing work on my music player muzika I started last month.
I’m very proud to say that though it’s still far from complete, it’s gotten to the point that I’ve already used it to listen to music while doing other stuff!

It only plays .flac files for now (though that is what my entire library is), has a playlist, and even has some basic controls (next/prev track, 5 seconds forward/back, and go to 0/10/20/…/90% point).
While working on it I considered pulling in a tui library, but after trying some out and not really liking any of them I decided against it. I thought about the api I wanted and realized that I basically just wanted some convenience functions for writing into a framebuffer, and after writing them I can confirm that its a joy to use. I’m hoping to flesh this out at some point and release it as its own thing.

And then I decided to make it integrate with the desktop so it can display what’s being played and so it can be controlled vie media keys. For that I needed dbus integration. I tried libdbus first, but the api felt like it would be a pain to work with in zig so that was a no go. Then I tried goose, the only zig implementation I was able to find in my brief search, and after a bad first impression I decided against it too.

So then, I kinda decided to uh…

Write one myself?

So this month I’ve been slowly reading through the dbus spec and implementing it. Currently I’m able to connect to dbus and immediately disconnect, and I also have a bunch of type marshaling logic done but not wired up to anything yet. I’m hoping to be able to send some messages by next week and to be able to implement the MPRIS interface by the end of the month. Hopefully also to be able to show it off not too long from now!

6 Likes

I’m new to Zig. And after I gave it a go to write the shell of my Rust OS kernel, I wanted to do a standalone project with it.
So I started doing a memory snapshot allocator called znapshot. It’s memfd and PAGEMAP_SCAN-based so the moment snapshot is called, the already-existing memory is marked as PRIVATE. So if those parts of memory is modified after the snapshot, Linux’s internal CoW mechanism kicks in and create a copy of those pages. restore is very simple where I just discard all the dirty pages and you are good to for your next iteration. And for commit, I use PAGEMAP_SCAN ioctl to go through the dirty pages and write them back to the original memfd region.

I also created a uffd-based version of it for testing but it’s substantially worse in the benchmarks. So it will just stay there probably alongside other methods (e.g. sigsegv handling) for a nice blogpost for the future.

Next step is to support nested checkpoints and rollbacks. Maybe some metrics for the future but haven’t really thought of it yet. But in general, I’m enjoying this as if I’m back in college and learning C for the first time. Very optimistic about Zig for now.

9 Likes

Keywisp - A small Wayland keystroke visualizer.

Recently I wrote a small tool called Keywisp in Zig, that displays your keystrokes on Wayland.

and also supports Waybar integration.

image

12 Likes
  • rewrote small utility to set a background message in x11 from c to zig: back-round
  • updated and upgraded, library to restrict which access a programm can have: restricted
  • started with building platform for roc in zig: restricted-roc
4 Likes

Yet another QOI decoder

const std = @import("std");

pub const Channels = enum(u8) {
    rgb = 3,
    rgba = 4,
};

pub const Colorspace = enum(u8) {
    srgb = 0,
    linear = 1,
};

pub const Header = struct {
    w: u32,
    h: u32,
    channels: Channels,
    colorspace: Colorspace,
};

const Op = packed struct(u8) {
    data: u6,
    tag: u2,

    fn rle(n: u6) @This() {
        std.debug.assert(n > 0 and n <= 62);
        return .{ .tag = 0b11, .data = n - 1 };
    }

    fn toInt(self: @This()) u8 {
        return @bitCast(self);
    }
};

const Pixel = packed struct(u32) {
    b: u8,
    g: u8,
    r: u8,
    a: u8,

    const black: @This() = .{ .r = 0, .g = 0, .b = 0, .a = 0xff };
    const transparent: @This() = .{ .r = 0, .g = 0, .b = 0, .a = 0 };

    fn hash(self: @This()) u6 {
        const vec: @Vector(4, u8) = .{ 7, 5, 3, 11 };
        return @truncate(@reduce(.Add, self.toVector() *% vec));
    }

    fn toVector(self: @This()) @Vector(4, u8) {
        return @bitCast(self);
    }

    fn diffRgb(self: @This(), data: u6) @This() {
        const Stream = packed struct(u6) { b: u2, g: u2, r: u2 };
        const diff: Stream = @bitCast(data);
        const bias: @Vector(4, i8) = .{ -2, -2, -2, 0 };
        const vec: @Vector(4, i8) = .{ diff.b, diff.g, diff.r, 0 };
        return @bitCast(self.toVector() +% @as(@Vector(4, u8), @bitCast(vec + bias)));
    }

    fn diffLuma(self: @This(), data: u6, extra: u8) @This() {
        const Stream = packed struct(u16) { g: u6, _: u2, b: u4, r: u4 };
        const payload: [2]u8 = .{ data, extra };
        const diff: Stream = @bitCast(payload);
        const g: @Vector(4, i8) = .{ diff.g, 0, diff.g, 0 };
        const bias: @Vector(4, i8) = .{ -40, -32, -40, 0 };
        const vec: @Vector(4, i8) = .{ diff.b, diff.g, diff.r, 0 };
        return @bitCast(self.toVector() +% @as(@Vector(4, u8), @bitCast(vec + g + bias)));
    }

    fn fromRgb(rgb: [3]u8, a: u8) @This() {
        return fromRgba(.{ rgb[0], rgb[1], rgb[2], a });
    }

    fn fromRgba(rgba: [4]u8) @This() {
        const swizzle: @Vector(4, u8) = .{ 2, 1, 0, 3 };
        return @bitCast(@shuffle(u8, rgba, undefined, swizzle));
    }
};

pub const Error = error{ InvalidHeader, InvalidRleChunk } || std.Io.Reader.Error || std.Io.Writer.Error;

/// Decode QOI image from source
/// Writes pixels in [31:0] A:R:G:B 8:8:8:8 format (BGRA little-endian) to the sink
pub fn decode(noalias source: *std.Io.Reader, noalias sink: *std.Io.Writer) Error!Header {
    var magic: [4]u8 = undefined;
    try source.readSliceAll(&magic);
    if (!std.mem.eql(u8, &magic, "qoif")) return error.InvalidHeader;

    const native_endian = @import("builtin").target.cpu.arch.endian();
    const hdr: Header = .{
        .w = try source.takeInt(u32, .big),
        .h = try source.takeInt(u32, .big),
        .channels = source.takeEnum(Channels, native_endian) catch return error.InvalidHeader,
        .colorspace = source.takeEnum(Colorspace, native_endian) catch return error.InvalidHeader,
    };

    const size = std.math.mul(usize, hdr.w, hdr.h) catch return error.InvalidHeader;
    const raw_size = std.math.mul(usize, size, @sizeOf(Pixel)) catch return error.InvalidHeader;
    const buffer: []Pixel = @alignCast(std.mem.bytesAsSlice(Pixel, (try sink.writableSliceGreedy(raw_size))[0..raw_size]));

    var index: usize = 0;
    var pixel: Pixel = .black;
    var lut: [64]Pixel = undefined;
    memset(Pixel, &lut, .transparent);
    loop: while (index < buffer.len) : (index += 1) {
        const op = try source.takeStruct(Op, native_endian);
        switch (op.toInt()) {
            0b11111110 => pixel = Pixel.fromRgb((try source.takeArray(3)).*, pixel.a),
            0b11111111 => pixel = Pixel.fromRgba((try source.takeArray(4)).*),
            0b00000000...0b00111111 => {
                pixel = lut[op.data];
                buffer[index] = pixel;
                continue :loop;
            },
            0b01000000...0b01111111 => pixel = pixel.diffRgb(op.data),
            0b10000000...0b10111111 => pixel = pixel.diffLuma(op.data, try source.takeByte()),
            Op.rle(1).toInt()...Op.rle(62).toInt() => {
                if (op.data + 1 > buffer.len - index) return error.InvalidRleChunk;
                memset(Pixel, buffer[index..][0 .. op.data + 1], pixel);
                index += op.data;
                continue :loop;
            },
        }
        buffer[index] = pixel;
        lut[pixel.hash()] = pixel;
    }

    sink.advance(index * @sizeOf(Pixel));
    std.debug.assert(sink.end == raw_size);
    return hdr;
}

// @memset is slow
fn memset(T: type, dst: []T, src: T) void {
    if (@import("builtin").mode == .ReleaseSmall) {
        for (dst) |*d| d.* = src;
    } else {
        const chunk: [@max(64 / @sizeOf(T), 1)]T = @splat(src);
        switch (dst.len) {
            0...chunk.len => |len| @memcpy(dst[0..len], chunk[0..len]),
            else => {
                var idx: usize = 0;
                while (idx < dst.len - chunk.len) : (idx += chunk.len) @memcpy(dst[idx..][0..chunk.len], chunk[0..]);
                @memcpy(dst[idx..], chunk[0 .. dst.len - idx]);
            },
        }
    }
}
6 Likes

I’ve been getting ready for SYCL 2026! I’ve been doing more of the boring stuff like logistics and zig master updates, but Spexguy has optimized frame rendering, introduced switched mode for the audio interface (PCM instead of basic square waves), came up with a 3D printed joystick design, and is currently adding Tracy support. Straight from the hardware and into the client!

16 Likes

Since its summer holiday time, I make a break from my s3 zig cli client which is mainly a job thing.

Now, I went back to my hobby project, my tui file manager lui.

When I find some time I’m working on a note management mode for lui to manage plain text notes (mostly markdown): https://codeberg.org/lukeflo/lui/src/branch/notes_management

Its planned as all in one successor of my tui notes management tool I hacked into fff.

Its fun and in the end I’ll have a file manager with integrated notes management all inside the terminal since I never was a fan of much too complicated note apps like e.g. Obsidian etc. I just don’t want to leave the terminal until its absolutely necessary :smile:

1 Like

BamOS

I’m still writing my Linux-compatible kernel (syscall compatibility) in Zig since 2024. This is a huge lie in the name; it’s not an OS, just a kernel for now =) Currently, it has basic subsystems like VFS (with devfs, tmpfs, initrd, ext2 drivers), memory management, processes/threads, a scheduler, a drivers subsystem, and a bunch of other minor things.

This month I’m implementing syscalls and things needed to run the Xorg server. I recently added signals and epoll calls, and now I’m moving on to Unix sockets.


Since this forum is about Zig, I want to express my major personal pain :slight_smile: : trying to make kernel module development in Zig possible. Unfortunately, Zig essentially lacks infrastructure for Zig-to-Zig dynamic libraries (kernel modules are basically dynamically loaded executables, like .so or .dll). I tried writing a binding generator, but it feels like I just want to strip all non-inline functions and implementations from the kernel, leaving everything else, since that’s exactly what you put in C/C++ header files. Exporting Zig functions is a massive pain - it’s easier to just rewrite everything in C. After long attempts, I realized it’s all just wild hacks. I’ve looked into the C3 language to rewrite the kernel, but I’m too used to Zig and not ready to rewrite everything from scratch yet… so for now, I’ll just leave it as is.

6 Likes

But wouldn’t this marry your kernel into whatever ABI zig decides? Personally I would not like that. I recommend instead creating actual ABI spec and generate bindings from that, this is what I do for one of my projects which allows you to load and run ELF files from any OS (callconv is also part of the ABI contract!). Ashet OS (another zig kernel) does something similar https://github.com/Ashet-Technologies/Ashet-OS/blob/master/src/abi/src/ashet.abi

1 Like

I’m trying to figure out how to get zig build (master) to let me use even an empty header file with translateC from Android Termux. Because that’s currently how I program on the go. And I need to go places, and I need to program Zig. :grinning_face_with_smiling_eyes:

I’m exploring idea of single-threaded zero-allocation reverse proxy. All current state of the art projects (like pingora for example) falls into worker threads and even work stealing, but is it right approach (given that reverse proxy should essentially move bytes)?

So far I get good results, but it is still an experiment and maybe I’m just lacking some security features (did you know about HTTP request smuggling?) or proper TLS handling.

Hey everyone! :waving_hand:

For the August edition, I wanted to share the progress on my Zig personal project: ZVector, a vector database engine built from scratch to be lightweight and fast.

What got done this month

Over the last few weeks, I focused heavily on the compute and memory performance layer:

  • SIMD Vectorization with @Vector: Implemented dot product and cosine distance calculations leveraging Zig’s native @Vector primitives. The performance gains for batch vector operations in memory have been huge.
  • Memory Alignment & Layout Optimization: Re-architected data structure alignments (align(64)) to line up cleanly with L1/L2 cache lines and eliminate allocation overhead.

The current bottleneck: Binary disk persistence

This is where things got tricky. Lately, I’ve hit a wall with serializing and storing the vector database state to disk in binary format.

My goal is to keep memory usage low by persisting the vector index directly to disk, but I’m running into two main challenges:

  1. Binary Layout & Alignment: Designing a compact binary format on disk without violating Zig’s strict memory alignment rules when reloading or reading data directly.
  2. I/O & Memory-Mapping: Finding the sweet spot between simple sequential writing (std.fs.File) and high-performance random reads (like mmap) for fast nearest-neighbor search without blowing up the RAM.

What’s next

If anyone here has worked on custom binary file formats or memory-mapped files (mmap) in Zig, I’d love to pick your brain after the talks!

Thanks for listening, and I can’t wait to hear what the rest of you have been building this month! :raising_hands: