Text, Sound, and Resources

Text communicates instructions and state, while sound gives immediate feedback. Both can be simple to use, but custom fonts and sounds are resources with lifetimes that your program must manage.

Table of Contents

  1. The Default Font
  2. Custom Fonts
  3. Initializing Audio
  4. Playing and Controlling a Sound
  5. Complete Example: Sound and Type
  6. Resource-Lifetime Checklist

The Default Font

DrawText() uses Raylib’s built-in default font. Its position is the top-left corner of the text:

DrawText("Hello, Raylib!", 40, 60, 30, DARKBLUE);

MeasureText() returns the width in pixels for that same default-font setup. This lets you centre text:

const char* message{"LEVEL COMPLETE"};
constexpr int fontSize{40};
const int textWidth{MeasureText(message, fontSize)};

DrawText(message,
         (GetScreenWidth() - textWidth) / 2,
         80,
         fontSize,
         DARKGREEN);

TextFormat() creates a temporary formatted string that is convenient for small labels:

DrawText(TextFormat("Score: %i", score), 20, 20, 24, BLACK);

Use std::string and normal C++ string-building tools when text must be stored or changed in more involved ways.

Custom Fonts

The examples use assets/JOKERMAN.TTF. Copy the module’s assets folder into the location expected by your course project.

Load a custom font after InitWindow(), draw it with DrawTextEx(), and unload it before CloseWindow():

Font headingFont{LoadFontEx("assets/JOKERMAN.TTF",
                            48, nullptr, 0)};

if (!IsFontValid(headingFont)) {
    TraceLog(LOG_ERROR, "Could not load the custom font");
    CloseWindow();
    return 1;
}

// Inside the drawing section of the main loop:
DrawTextEx(headingFont, "NEON ARCADE", Vector2{30.0F, 30.0F},
           48.0F, 2.0F, MAGENTA);

// After the main loop:
UnloadFont(headingFont);
CloseWindow();

The last two numeric arguments to DrawTextEx() are font size and character spacing, both in pixels. MeasureTextEx() returns a Vector2 containing the measured width and height.

Initializing Audio

The audio device is separate from the window. Initialize it before loading sounds:

InitWindow(800, 450, "Sound Example");
InitAudioDevice();

Sound coin{LoadSound("assets/coin.wav")};

At shutdown, release resources first, then close the device that owns them:

UnloadSound(coin);
CloseAudioDevice();
CloseWindow();

Do not close the audio device while a loaded Sound still depends on it.

Playing and Controlling a Sound

These functions cover the most common sound controls:

PlaySound(coin);                 // Start from the beginning.
StopSound(coin);                 // Stop playback.
PauseSound(coin);                // Pause at the current position.
ResumeSound(coin);               // Continue a paused sound.
SetSoundVolume(coin, 0.5F);      // 0.0 is silent; 1.0 is full volume.
SetSoundPitch(coin, 1.2F);       // 1.0 is the original pitch.

const bool playing{IsSoundPlaying(coin)};

PlaySound() is a natural match for IsKeyPressed() or a collision that occurs once. Calling it every frame while a key is held restarts the sound repeatedly.

Complete Example: Sound and Type

This program uses the included drum loop and custom font:

#include "raylib.h"
#include <algorithm>

int main() {
    InitWindow(450, 180, "Raylib - Sound and Type");
    InitAudioDevice();

    bool assetsLoaded{ true };
    float volume{ 1.0F };

    Font font{ LoadFontEx("assets/JOKERMAN.TTF", 48, nullptr, 0) };
    if (!IsFontValid(font)) {
        TraceLog(LOG_ERROR, "Could not load the custom font");
        assetsLoaded = false;
    }

    Sound loop{ LoadSound("assets/kammerin-hunt-drum-loop.mp3") };
    if (!IsSoundValid(loop)) {
        TraceLog(LOG_ERROR, "Could not load the drum loop");
        assetsLoaded = false;
    }

    while (!WindowShouldClose() && assetsLoaded) {
        if (IsKeyPressed(KEY_P)) PlaySound(loop);
        if (IsKeyPressed(KEY_S)) StopSound(loop);
        if (IsKeyPressed(KEY_SPACE)) {
            if (IsSoundPlaying(loop)) {
                PauseSound(loop);
            }
            else {
                ResumeSound(loop);
            }
        }

        if (IsKeyPressed(KEY_UP)) volume += 0.1F;
        if (IsKeyPressed(KEY_DOWN)) volume -= 0.1F;
        volume = std::clamp(volume, 0.0F, 1.0F);
        SetSoundVolume(loop, volume);

		const Color background{ IsSoundPlaying(loop) ? DARKGREEN : SKYBLUE };

        BeginDrawing();
        ClearBackground(background);

        DrawTextEx(font, "SOUND + TYPE", Vector2{ 20.0F, 20.0F },
            52.0F, 2.0F, RAYWHITE);
        DrawText("P: play   Space: pause/resume   S: stop",
            20, 90, 20, RAYWHITE);
        DrawText(TextFormat("Volume: %i%%  (up/down)",
            static_cast<int>(volume * 100.0F)),
            20, 130, 20, RAYWHITE);

        EndDrawing();
    }

    UnloadSound(loop);
    UnloadFont(font);
    CloseAudioDevice();
    CloseWindow();
}

Resource-Lifetime Checklist

  • Call InitWindow() before loading textures, fonts, or render textures.
  • Call InitAudioDevice() before loading sounds.
  • Check loaded resources with functions such as IsTextureValid(), IsFontValid(), and IsSoundValid().
  • Load once before the main loop. Do not load the same file every frame.
  • Keep each resource alive while it is being used.
  • Unload each successfully loaded resource exactly once.
  • Unload graphics resources before CloseWindow().
  • Unload sounds before CloseAudioDevice().

Resources