Audio Troubles In Your Raylib Web Build? Try These Solutions.


I've recently finished making a game, Dodger, using a C library named Raylib. If you're unfamiliar, Raylib is a simple C library that has some useful functions for building games, or mainly, drawing things to the screen and opening a window with an OpenGL context, which can be a pain to do from scratch in C or C++. Since Raylib is only a library, and not a game engine, building anything with Raylib, especially a game, requires a lot more programming than it otherwise would if you used something like Unity or Unreal. However, Raylib provides a simple and lightweight way to code for those who don't want, or need, a giant game engine. 

One problem I faced when making Dodger was getting audio to work properly with my WebAssembly build. Again, since Raylib is only a library, there is no "Build for HTML" button to speak of. So if you want to build your game for the web you'll need to use a separate tool, like Emscripten. Emscipten compiles your C or C++ code into WebAssembly code, but to do so, you'll need to first install Emscripten and pass it the right compilation flags. This is where things began to get complicated for me. Because I will be focusing on the audio aspect of this project I won't be covering every detail of building with Emscipten, instead, check out Emsripten.org or the Raylib Wiki for general details of how to build your project with the tool.

Check your compilation flags.

The Raylib Wiki recommends you run this line to compile your code via the command line.

emcc -o game.html game.c -Os -Wall ./path-to/libraylib.a -I. -Ipath-to-raylib-h -L. -Lpath-to-libraylib-a -s USE_GLFW=3 -s ASYNCIFY --shell-file path-to/shell.html -DPLATFORM_WEB

If you run only this command line, subbing in your specific paths, you will likely have issues with your build. The line above will work, but only if your game does not use any resource loading, which isn't likely. If your game loads any resources like images, textures, audio, fonts, or models you'll need to add some additional flags. The Wiki recommends these additional flags:

--preload-file resources      
// Specify a resources directory for data compilation (it will generate a .data file)   
-s TOTAL_MEMORY=67108864      
// Specify a heap memory size in bytes (default = 16MB) (67108864 = 64MB)   
-s ALLOW_MEMORY_GROWTH=1      
// Allow automatic heap memory resizing -> NOT RECOMMENDED!   
-s FORCE_FILESYSTEM=1         
// Force filesystem creation to load/save files data (for example if you need to support save-game or drag&drop files)   
-s ASSERTIONS=1               
// Enable runtime checks for common memory allocation errors (-O1 and above turn it off)   
--profiling                   
// Include information for code profiling

Of these flags --preload-file resources, -s TOTAL_MEMORY=67108864 and -s FORCE_FILESYSTEM=1 were imperative to my game. Dodger has six different songs located in a folder named Music. Without the flag --preload-file path/to/Music/, my music would not be compiled into a .data file and would not be accessible in my Web build. There is one more vital compilation flag that is not listed here or by the Raylib Wiki which I cover later. See, "Include ccall() when compiling your build."

Hopefully, you have at least a functioning WebAssembly build by this point because we'll need to do some troubleshooting in the web browser. Follow step 5 on the Wiki to get your build working on your local machine in the browser. Then open up the browser's developer tools and take a look at the console output for useful errors.

Assets fail to load or can not be opened.

*Edit: Alternatively, you can use this command `--preload-file original-path@destination-path` to explicitly specify where the resource will be located in the virtual file system. Read more about the @ symbol and packaging files on this Emscripten page. Credit: @raysan5

This was a tricky bug for me to sort out, but like many terrible bugs, the solution to this one was quite simple, once I finally discovered it. For me, the fix was changing the directory path in which my music was located. In the desktop build of my game the music path is simply "Music", but for the Web build I needed to change it to "dir/Music". Why the change? Well, it turns out that your "Working Directory" may not be the same in your Web build as it is in your Desktop build. If my folder structure looks like this: dir1/dir2/Music and my working directory is dir2, I can reach Music with the path "Music". But if my working directory is dir1 I need to use the path dir2/Music to reach the same folder! 

Be sure to check your working directory with the function GetWorkingDirectory() and output it to the console, build your project, run it in the browser, and check the console output. Update your resource path accordingly.



Include ccall() when compiling your build.

*Edit: I'm told if you're using the master branch of Raylib, this issue has been fixed. Consider using the master branch instead of the 4.2.0 release version (at the time of writing) if you're having problems, otherwise read on. Credit: @raysan5

One thing I regret not doing sooner is looking into the error messages I was receiving about a symbol named 'ccall'. Because my assets were loading successfully and my game was playable I mistook another error for being the reason my game was not playing sound when instead it was this one (I'll talk about that red herring next). If you notice errors in your console log complaining about a symbol named 'ccall' then add this compilation flag:

-sEXPORTED_RUNTIME_METHODS=ccall,cwrap

If you're interested in more details, or if this didn't fix your ccall() errors, check out this Emscripten page regarding ccall().

After implementing these solutions I was finally able to hear the amazing electronic tracks of Dodger. It was a very satisfying and relieving feeling! Before I wrap things up though I want to talk about one last error, one that you will likely encounter if your game plays audio.

Audio is not allowed to start.


This error had me convinced it was the reason I couldn't hear my audio and sent me on a wild goose chase that wasted quite a bit of my time. Today, most browsers block audio content from being played automatically, to prevent sneaky ads and popups from automatically playing sound and annoying users. In order to get your sound unblocked, the user must first use a "gesture" on the page, i.e. click somewhere on the page your audio source is embedded in. This led me to believe my game's audio was not being emitted because the browser was blocking it.

 I searched google for answers and found some suggestions and solutions stating you should initialize your audio device after the user clicks the screen or other Web-specific ways to initialize audio. I spent a while trying to figure out where and how to initialize my audio (that was originally initialized on startup) so that it could play in the browser. I tried wrapping the initialization in a key press or a click, I tried initializing it in different game states and putting it in the update loop, but nothing worked. I could get the warning to go away, but still, my sound did not play. Eventually, I did get sound to play, via the methods listed above, and I realized I could revert all my audio code to how it was originally, and add one simple change. Require the user to click the page to continue at the title screen. 

My advice, look at the Raylib audio examples and follow suit, then add a click condition to your title screen. 

// Pseudocode
main { // Do this
    InitAudio()
    Music song = LoadMusicStream("musicpath")
    PlayMusicStream(song)
    while(gameloop) { 
        UpdateMusicStream(song)
        LoadTitleScreen()
        if(userClick) { 
            StartGame()
        } 
    }
}
main {  // Not This
    while(game loop) { 
        if(userClick) { 
            InitAudio()
            Music song = LoadMusicStream("musicpath")
            PlayMusicStream(Song)
            UpdateMusicStream(song)
            StartGame()
        }
    }
}

Wrap Up

I covered a lot in this devlog so I'd like to do a quick recap.

  • Make sure you use the --preload-file /pathto/resources flag
  • Check to make sure your paths are correct. Use GetWorkingDirectory() to figure out your starting path.
  • If you see ccall() errors in the console log add the compilation flag -sEXPORTED_RUNTIME_METHODS=ccall,cwrap
  • Initialize your audio regularly, just add a click condition to your title screen or state.

I sincerely hope this devlog and the solutions provided help you get working audio in your Raylib project. If you have any questions, or feedback please, feel free to share. Check out my games here on itch (they're all free), as well as my Twitter @JusticeBole. You can also view all the source code for Dodger on my Github. Special thanks to @raysan5 for providing great feedback and alternative solutions.

Files

dodger_game_web.zip Play in browser
Dec 06, 2022

Leave a comment

Log in with itch.io to leave a comment.