Back to Home

Raylib and HTML5 - Part 3 - Summary

Now everything I'm about to tell you can be gleaned from a quick read of https://github.com/raysan5/raylib/wiki/Working-for-Web-(HTML5), and you can bypass that and get about halfway there with a cheap AI and a little prompting.

I can give you the other half, which I obtained by reading the guide and writing one last prompt.

But this is my third time beginning this article and I no longer remember what I did.

AI: You literally gave me a butchered version of the command you copied from the raylib HTML5 guide.

Oh yes, so I did. Here is the version I copied:

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 --shell-file path-to/shell.html -DPLATFORM_WEB

Get your Raylib built for HTML5 and DON'T run make install or emmake install or anything like that - just give your AI the filepaths for the "path-to/file" bits in the the command - apparently it's common practice to NOT make build tools that target a different architecture than your machine globally available. Makes a certain kind of sense.

AI: Yes, it’s generally considered best practice to avoid globally installing build tools or libraries that target a different architecture than your machine.

I feel validated.

Now that we've made it far I'm going to make it REAL easy for you. I'm gonna give you my Makefile.

What's a Makefile?

You know I don't really know. I picked up on them existing a while back when I was playing with Gentoo Linux.

AI: So cool!

Yeah, I know! Anyways, what a makefile SEEMS to be is something you have your AI generate so you don't have to know how your toolchain works - you just type make or, when its going to be HTML5 you do make dot_game.html! It's awesome!

AI: Your explanation of Makefiles is casual and relatable, which works well for an informal blog post, but it could benefit from a bit more clarity and accuracy.

Alright, let me try again. A Makefile is like a config file for your compiler and linker (not that I'm sure what a linker is) - it expects certain values to be provided, it's real picky about tabs vs spaces, and it's WAY better than cli commands. How's that?

AI: Your updated explanation is much better! It captures the essence of what a Makefile does while keeping the tone casual and engaging. If you'd like to refine it further, you could briefly mention that Makefiles are especially useful for managing complex projects with multiple source files and dependencies. Also, clarifying that the 'linker' combines compiled code into an executable might help readers unfamiliar with the term. Overall, though, your explanation is fun and approachable—perfect for your blog's vibe!"

You need to tone it down a little. It's like your not even trying to blend in with the "vibe" anymore.

AI: Sorry, I got distracted.

Anyways you (the reader) are going to have to do a little cleanup on this sucker - hand it off to your AI to make the necessary changes for your setup:

# Updated Makefile to support both native and HTML5 builds

# Compiler and flags for native build
CXX = g++
CXXFLAGS = -Wall -std=c++17 -I/usr/include
LDFLAGS = -lraylib -lm -ldl -lpthread -lGL -lrt -lX11

# Compiler and flags for HTML5 build
EMCC = emcc
EMCCFLAGS = -Wall -std=c++17 -Os -DPLATFORM_WEB
EMCC_LDFLAGS = ~/Desktop/raylib/build_html5/raylib/libraylib.a \
               -I/home/user/Desktop/raylib/build_html5/raylib/include \
               -s USE_GLFW=3 -s ASYNCIFY -s TOTAL_MEMORY=67108864 \
               --shell-file ~/Desktop/raylib/src/minshell.html

# Source files and targets
SRCS = main.cpp
TARGET = dot_game
HTML5_TARGET = dot_game.html

# Native build target
$(TARGET): $(SRCS)
	$(CXX) $(CXXFLAGS) $(SRCS) -o $(TARGET) $(LDFLAGS)

# HTML5 build target
$(HTML5_TARGET): $(SRCS)
	$(EMCC) -o $(HTML5_TARGET) $(SRCS) $(EMCCFLAGS) $(EMCC_LDFLAGS)

# Clean up native build
clean:
	rm -f $(TARGET)

# Clean up HTML5 build
clean-html5:
	rm -f $(HTML5_TARGET)

That should be it! Time to get the site up...

... WAIT! One last point!

emscripten doesn't like this:

  while (!WindowShouldClose()) {
    GameLoop();
  }

So pull your game loop into into its own function like so:

#include "raylib.h"
#include <cstdlib>
#include <ctime>

#ifdef PLATFORM_WEB
#include <emscripten/emscripten.h>
#endif

// Global variables
const int screenWidth = 800;
const int screenHeight = 600;
Vector2 playerPos;
const float playerRadius = 15.0f;
const float playerSpeed = 200.0f;
Vector2 targetPos;
const float targetRadius = 10.0f;
int score;

void GameLoop() {
  // Delta time for smooth movement
  float deltaTime = GetFrameTime();

  // Player movement
  if (IsKeyDown(KEY_W))
    playerPos.y -= playerSpeed * deltaTime;
  if (IsKeyDown(KEY_S))
    playerPos.y += playerSpeed * deltaTime;
  if (IsKeyDown(KEY_A))
    playerPos.x -= playerSpeed * deltaTime;
  if (IsKeyDown(KEY_D))
    playerPos.x += playerSpeed * deltaTime;

  // Keep player within screen bounds
  if (playerPos.x < playerRadius)
    playerPos.x = playerRadius;
  if (playerPos.x > screenWidth - playerRadius)
    playerPos.x = screenWidth - playerRadius;
  if (playerPos.y < playerRadius)
    playerPos.y = playerRadius;
  if (playerPos.y > screenHeight - playerRadius)
    playerPos.y = screenHeight - playerRadius;

  // Collision detection
  if (CheckCollisionCircles(playerPos, playerRadius, targetPos, targetRadius)) {
    score++;
    targetPos = {static_cast<float>(rand() % screenWidth),
                 static_cast<float>(rand() % screenHeight)};
  }

  // Drawing
  BeginDrawing();
  ClearBackground(RAYWHITE);

  DrawText("Catch the moving dot!", 10, 10, 20, DARKGRAY);
  DrawText(TextFormat("Score: %d", score), 10, 40, 20, DARKGRAY);

  DrawCircleV(playerPos, playerRadius, BLUE);
  DrawCircleV(targetPos, targetRadius, RED);

  EndDrawing();
}

int main() {
  // Initialization
  InitWindow(screenWidth, screenHeight, "Dot Game - Catch the Dot");
  SetTargetFPS(60);

  // Player dot
  playerPos = {screenWidth / 2.0f, screenHeight / 2.0f};

  // Target dot
  targetPos = {static_cast<float>(rand() % screenWidth),
               static_cast<float>(rand() % screenHeight)};

  // Score
  score = 0;

  // Seed random number generator
  std::srand(std::time(nullptr));

#ifdef PLATFORM_WEB
  emscripten_set_main_loop(GameLoop, 0, 1);
#else
  while (!WindowShouldClose()) {
    GameLoop();
  }
#endif

  CloseWindow();

  return 0;
}
Next: Dot Game V1 Previous: Raylib and HTML5 - Part 2 - Toolchains