animation timing
There is no browser build of this example yet.
Is it the same as C?
- Desktop:verified The Zig version renders a final frame that is byte-identical to the C example.
- Browser:not checked yet The browser comparison does not cover this example yet.
//! raylib [models] example - animation timing
//!
//! Zig-API translation of examples/models/models_animation_timing.c (raylib 6.2).
//! Example licensed under an unmodified zlib/libpng license.
//! Copyright (c) 2026 Ramon Santamaria (@raysan5)
const rl = @import("raylib");
const gui = @import("raygui");
pub fn main() !void {
// Initialization
const screen_width = 800;
const screen_height = 450;
rl.initWindow(screen_width, screen_height, "raylib [models] example - animation timing");
defer rl.closeWindow(); // Close window and OpenGL context
// Define the camera to look into our 3d world
var camera: rl.Camera = .{
.position = .{ .x = 6.0, .y = 6.0, .z = 6.0 }, // Camera position
.target = .{ .x = 0.0, .y = 2.0, .z = 0.0 }, // Camera looking at point
.up = .{ .x = 0.0, .y = 1.0, .z = 0.0 }, // Camera up vector (rotation towards target)
.fovy = 45.0, // Camera field-of-view Y
.projection = .perspective, // Camera projection type
};
// Load model
const model = try rl.loadModel("resources/models/gltf/robot.glb");
defer rl.unloadModel(model); // Unload model and meshes/material
const position: rl.Vector3 = .{ .x = 0.0, .y = 0.0, .z = 0.0 }; // Set model world position
// Load model animations
const anims = try rl.loadModelAnimations("resources/models/gltf/robot.glb");
defer rl.unloadModelAnimations(anims); // Unload model animation
// Animation playing variables
var anim_index: i32 = 10; // Current animation playing
var anim_current_frame: f32 = 0.0; // Current animation frame (supporting interpolated frames)
var anim_frame_speed: f32 = 0.5; // Animation play speed
var anim_pause = false; // Pause animation
// UI required variables
var anim_names_buffer: [64][*:0]const u8 = undefined;
const anim_names = anim_names_buffer[0..anims.len];
for (anim_names, anims) |*name, *anim| name.* = @ptrCast(&anim.name);
var dropdown_edit_mode = false;
var anim_frame_progress: f32 = 0.0;
rl.setTargetFPS(60); // Set our game to run at 60 frames-per-second
// Main game loop
while (!rl.windowShouldClose()) { // Detect window close button or ESC key
// Update
rl.updateCamera(&camera, .orbital);
if (rl.isKeyPressed(.p)) anim_pause = !anim_pause;
if (!anim_pause and anim_index < anims.len) {
// Update model animation
const anim = anims[@intCast(anim_index)];
anim_current_frame += anim_frame_speed;
if (anim_current_frame >= @as(f32, @floatFromInt(anim.keyframeCount))) anim_current_frame = 0.0;
rl.updateModelAnimation(model, anim, anim_current_frame);
}
// NOTE: Animation and playing speed selected through UI
// Update progressbar value with current frame
anim_frame_progress = anim_current_frame;
// Draw
rl.beginDrawing();
defer rl.endDrawing();
rl.clearBackground(.ray_white);
{
rl.beginMode3D(camera);
defer rl.endMode3D();
rl.drawModel(model, position, 1.0, .white);
rl.drawGrid(10, 1.0);
}
// Draw UI, select anim and playing speed
gui.setStyle(.dropdownbox, gui.DropdownBoxProperty.dropdown_items_spacing, 1);
if (gui.dropdownBox(.{ .x = 10, .y = 10, .width = 140, .height = 24 }, rl.textJoin(anim_names, ";"), &anim_index, dropdown_edit_mode) != 0) {
dropdown_edit_mode = !dropdown_edit_mode;
}
_ = gui.slider(.{ .x = 260, .y = 10, .width = 500, .height = 24 }, "FRAME SPEED: ", rl.textFormat("x{d:.1}", .{anim_frame_speed}), &anim_frame_speed, 0.1, 2.0);
// Draw playing timeline with keyframes
const keyframe_count = anims[@intCast(anim_index)].keyframeCount;
const screen_w = rl.getScreenWidth();
const screen_h = rl.getScreenHeight();
_ = gui.label(
.{ .x = 10, .y = @as(f32, @floatFromInt(screen_h)) - 64.0, .width = @as(f32, @floatFromInt(screen_w)) - 20.0, .height = 24 },
rl.textFormat("CURRENT FRAME: {d:.2} / {d}", .{ anim_frame_progress, keyframe_count }),
);
_ = gui.progressBar(
.{ .x = 10, .y = @as(f32, @floatFromInt(screen_h)) - 40.0, .width = @as(f32, @floatFromInt(screen_w)) - 20.0, .height = 24 },
null,
null,
&anim_frame_progress,
0.0,
@floatFromInt(keyframe_count),
);
const keyframe_spacing = @as(f32, @floatFromInt(screen_w - 20)) / @as(f32, @floatFromInt(keyframe_count));
for (0..@intCast(keyframe_count)) |i| {
const x: i32 = @intFromFloat(keyframe_spacing * @as(f32, @floatFromInt(i)));
rl.drawRectangle(10 + x, screen_h - 40, 1, 24, .blue);
}
}
}/*******************************************************************************************
*
* raylib [models] example - animation timing
*
* Example complexity rating: [★★★☆] 3/4
*
* Example originally created with raylib 6.0, last time updated with raylib 6.0
*
* Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
* BSD-like license that allows static linking with closed source software
*
* Copyright (c) 2026 Ramon Santamaria (@raysan5)
*
********************************************************************************************/
#include "raylib.h"
#define RAYGUI_IMPLEMENTATION
#include "raygui.h" // Required for: UI controls
//------------------------------------------------------------------------------------
// Program main entry point
//------------------------------------------------------------------------------------
int main(void)
{
// Initialization
//--------------------------------------------------------------------------------------
const int screenWidth = 800;
const int screenHeight = 450;
InitWindow(screenWidth, screenHeight, "raylib [models] example - animation timing");
// Define the camera to look into our 3d world
Camera camera = { 0 };
camera.position = (Vector3){ 6.0f, 6.0f, 6.0f }; // Camera position
camera.target = (Vector3){ 0.0f, 2.0f, 0.0f }; // Camera looking at point
camera.up = (Vector3){ 0.0f, 1.0f, 0.0f }; // Camera up vector (rotation towards target)
camera.fovy = 45.0f; // Camera field-of-view Y
camera.projection = CAMERA_PERSPECTIVE; // Camera projection type
// Load model
Model model = LoadModel("resources/models/gltf/robot.glb");
Vector3 position = { 0.0f, 0.0f, 0.0f }; // Set model world position
// Load model animations
int animCount = 0;
ModelAnimation *anims = LoadModelAnimations("resources/models/gltf/robot.glb", &animCount);
// Animation playing variables
int animIndex = 10; // Current animation playing
float animCurrentFrame = 0.0f; // Current animation frame (supporting interpolated frames)
float animFrameSpeed = 0.5f; // Animation play speed
bool animPause = false; // Pause animation
// UI required variables
char *animNames[64] = { 0 };
for (int i = 0; i < animCount; i++) animNames[i] = anims[i].name;
bool dropdownEditMode = false;
float animFrameProgress = 0.0f;
SetTargetFPS(60); // Set our game to run at 60 frames-per-second
//--------------------------------------------------------------------------------------
// Main game loop
while (!WindowShouldClose()) // Detect window close button or ESC key
{
// Update
//----------------------------------------------------------------------------------
UpdateCamera(&camera, CAMERA_ORBITAL);
if (IsKeyPressed(KEY_P)) animPause = !animPause;
if (!animPause && (animIndex < animCount))
{
// Update model animation
animCurrentFrame += animFrameSpeed;
if (animCurrentFrame >= anims[animIndex].keyframeCount) animCurrentFrame = 0.0f;
UpdateModelAnimation(model, anims[animIndex], animCurrentFrame);
}
// NOTE: Animation and playing speed selected through UI
// Update progressbar value with current frame
animFrameProgress = animCurrentFrame;
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(RAYWHITE);
BeginMode3D(camera);
DrawModel(model, position, 1.0f, WHITE);
DrawGrid(10, 1.0f);
EndMode3D();
// Draw UI, select anim and playing speed
GuiSetStyle(DROPDOWNBOX, DROPDOWN_ITEMS_SPACING, 1);
if (GuiDropdownBox((Rectangle){ 10, 10, 140, 24 }, TextJoin(animNames, animCount, ";"),
&animIndex, dropdownEditMode)) dropdownEditMode = !dropdownEditMode;
GuiSlider((Rectangle){ 260, 10, 500, 24 }, "FRAME SPEED: ", TextFormat("x%.1f", animFrameSpeed),
&animFrameSpeed, 0.1f, 2.0f);
// Draw playing timeline with keyframes
GuiLabel((Rectangle){ 10, GetScreenHeight() - 64.0f, GetScreenWidth() - 20.0f, 24 },
TextFormat("CURRENT FRAME: %.2f / %i", animFrameProgress, anims[animIndex].keyframeCount));
GuiProgressBar((Rectangle){ 10, GetScreenHeight() - 40.0f, GetScreenWidth() - 20.0f, 24 }, NULL, NULL,
&animFrameProgress, 0.0f, (float)anims[animIndex].keyframeCount);
for (int i = 0; i < anims[animIndex].keyframeCount; i++)
DrawRectangle(10 + (int)(((float)(GetScreenWidth() - 20)/(float)anims[animIndex].keyframeCount)*(float)i),
GetScreenHeight() - 40, 1, 24, BLUE);
EndDrawing();
//----------------------------------------------------------------------------------
}
// De-Initialization
//--------------------------------------------------------------------------------------
UnloadModelAnimations(anims, animCount); // Unload model animation
UnloadModel(model); // Unload model and meshes/material
CloseWindow(); // Close window and OpenGL context
//--------------------------------------------------------------------------------------
return 0;
}
From raylib 6.2. The original on GitHub.