I wrote a small library to display loading spinners in the terminal and I’d like to share it with you ![]()
Before I waffle about it, here is a quick demo:

Twirl comes with several different loading spinner styles, most of which are featured in the GIF you see up above.
Usage looks something like this:
pub fn main() !void {
var writer = std.fs.File.stdout().writer("");
// This is where you choose the visual style of the spinner.
// VVVVVVVVVV
var spinner: twirl.Spinner = .of(.ascii_bar);
// This sets the framerate. You can also use `loopsPerMinute`.
// VVVVVVVVVVVVVVVVVVV
spinner.framesPerSecond(10);
// This waits for the next frame before continuing.
// VVVVVVVVVVVVV
while (true) : (spinner.waitAndNext()) {
// Spinners can be printed to the console using the `{f}` formatter:
try writer.interface.print(at(1, 1) ++ "{f} Waiting for file 'test' to exist...", .{spinner});
std.fs.cwd().access("test", .{}) catch |e| switch (e) {
error.FileNotFound => continue,
else => return e,
};
try writer.interface.writeAll("Found it!");
break;
}
}
/// Move terminal cursor to specified row and column.
fn at(comptime x: usize, comptime y: usize) []const u8 {
return std.fmt.comptimePrint("\x1B[{d};{d}H", .{ y, x });
}
There are three modes of usage, which are best explained by taking a look at the examples. examples/showcase.zig produces what is shown in the GIF above. The other three examples show you how to use twirl.
There shouldn’t be a situation where twirl can’t work. If there is, let me know!
Also, if you have a style in mind that I haven’t implemented yet, please tell me about it! I’d like to add more styles ![]()
Anyway, let me know what you think of the project and of the code. Feedback much appreciated!