Skip to content

Instantly share code, notes, and snippets.

@doccaico
Created July 17, 2026 12:44
Show Gist options
  • Select an option

  • Save doccaico/d7fc03524393d7c1e134aacaed49e66d to your computer and use it in GitHub Desktop.

Select an option

Save doccaico/d7fc03524393d7c1e134aacaed49e66d to your computer and use it in GitHub Desktop.
Raylib + Emscripten using HTML's select
@echo off
setlocal
set OUT_DIR=web
if not exist %OUT_DIR% mkdir %OUT_DIR%
set FILES="vendor\raylib\bin\wasm\libraylib.web.a"
set "FLAGS=-DPLATFORM_WEB"
set "FLAGS=%FLAGS% -I ./vendor/raylib/include"
set "FLAGS=%FLAGS% -sEXPORTED_RUNTIME_METHODS=['HEAPF32'] -sUSE_GLFW=3 -sWASM_BIGINT -sWARN_ON_UNDEFINED_SYMBOLS=0 -sEXPORTED_RUNTIME_METHODS=ccall --shell-file src\index_template.html"
if "%~1" == "--debug" (
set "FLAGS=%FLAGS% -sASSERTIONS=1 -sSTACK_OVERFLOW_CHECK=1"
) else if "%~1" == "--release" (
set "FLAGS=%FLAGS% -Os -flto"
)
cmd /c emcc -o %OUT_DIR%\index.html src\main.c %FLAGS% %FILES%
echo Web build created in %OUT_DIR%
endlocal
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// #include <time.h>
// #include "types.h"
#include "defines.h"
// #include "input_handling.h"
#include "raylib.h"
#if defined(PLATFORM_WEB)
#include <emscripten/emscripten.h>
#endif
typedef enum {
PET_CAT = 0,
PET_DOG = 1,
} PetType;
PetType pet_type = PET_CAT;
EMSCRIPTEN_KEEPALIVE
void on_select_changed(const char* value) {
if (strcmp(value, "cat") == 0) {
pet_type = PET_CAT;
} else if (strcmp(value, "dog") == 0) {
pet_type = PET_DOG;
}
printf("Received value: %s\n", value);
printf("Current pet_type: %d\n", pet_type);
}
static void init_game(void) {
}
static void deinit_game() {
}
static void update_game(void)
{
BeginDrawing();
ClearBackground(WHITE);
if (pet_type == PET_CAT) {
DrawText("Cat", 100, 300, 25, BLACK);
} else if (pet_type == PET_DOG) {
DrawText("Dog", 100, 300, 25, BLACK);
}
EndDrawing();
}
int main(void)
{
InitWindow(WINDOW_WIDTH, WINDOW_HEIGHT, WINDOW_TITLE);
init_game();
#if defined(PLATFORM_WEB)
// Hand over loop execution control to the browser.
// 0 = Use browser's preferred FPS (requestAnimationFrame).
// 1 = Simulate an infinite block loop.
emscripten_set_main_loop(update_game, 0, 1);
#else
// Traditional desktop game loop capped at 60 FPS
SetTargetFPS(60);
while (!WindowShouldClose())
{
update_game();
}
#endif
#if !defined(PLATFORM_WEB)
deinit_game();
#endif
CloseWindow();
return 0;
}
<!doctype html>
<html lang="EN-us">
<head>
<meta charset="utf-8">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>Simple CGOL</title>
<meta name="title" content="Simple CGOL">
<meta name="description" content="A single player game invented in 1970 by Cambridge mathematician John Conway">
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
<style>
html, body {
margin: 0 !important;
padding: 0 !important;
width: 100vw !important;
height: 100dvh !important;
background-color: #1a1a24;
overflow: hidden !important;
/* 上下左右の完全に真ん中に要素を集める設定 */
display: flex !important;
justify-content: center !important;
align-items: center !important;
}
/* 1. Canvasのサイズにぴったり合わせる基準コンテナ */
.game-wrapper {
position: relative !important; /* 中のselectの位置基準になります */
/* Canvasと全く同じサイズ制限を持たせる */
display: flex !important;
width: auto !important;
height: auto !important;
max-width: 100vw !important;
max-height: 100dvh !important;
aspect-ratio: 2 / 3 !important;
}
/* 2. Canvas自体を画面の中央に固定する設定 */
canvas.emscripten {
border: 0px none;
background-color: black;
display: block !important;
width: 100% !important; /* 親のwrapperいっぱいに広げる */
height: 100% !important; /* 親のwrapperいっぱいに広げる */
image-rendering: pixelated;
}
/* 3. 白い画面の左上に重ねる設定 */
#pet-select {
font-size: 18px; /* 文字を20pxに拡大 */
padding: 4px 8px; /* 見栄えを良くするための余白 */
position: absolute !important;
top: 5px; /* 白い画面の上端からの隙間 */
left: 5px; /* 白い画面の左端からの隙間 */
z-index: 999; /* 最前面に表示 */
}
</style>
<script type='text/javascript' src="https://cdn.jsdelivr.net/gh/eligrey/FileSaver.js/dist/FileSaver.min.js"> </script>
<script type='text/javascript'>
function saveFileFromMEMFSToDisk(memoryFSname, localFSname) // This can be called by C/C++ code
{
var isSafari = false; // Not supported, navigator.userAgent access is being restricted
//var isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
var data = FS.readFile(memoryFSname);
var blob;
if (isSafari) blob = new Blob([data.buffer], { type: "application/octet-stream" });
else blob = new Blob([data.buffer], { type: "application/octet-binary" });
// NOTE: SaveAsDialog is a browser setting. For example, in Google Chrome,
// in Settings/Advanced/Downloads section you have a setting:
// 'Ask where to save each file before downloading' - which you can set true/false.
// If you enable this setting it would always ask you and bring the SaveAsDialog
saveAs(blob, localFSname);
// Alternative implementation to avoid FileSaver.js
//const link = document.createElement("a");
//link.href = URL.createObjectURL(blob);
//link.download = localFSname;
//link.click();
}
</script>
</head>
<body>
<div class="game-wrapper">
<select name="pets" id="pet-select">
<option value="cat" selected>I'm cat and don't sleep</option>
<option value="dog">Rotation circle</option>
</select>
<canvas class=emscripten id=canvas oncontextmenu=event.preventDefault() tabindex=-1></canvas>
</div>
<p id="output" />
<script>
var Module = {
canvas: (function() {
var canvas = document.getElementById('canvas');
return canvas;
})(),
};
document.getElementById('pet-select').addEventListener('change', function() {
var selectedValue = this.value;
// C側の関数 `on_select_changed` を呼び出す (事前バインドが必要)
Module.ccall('on_select_changed', null, ['string'], [selectedValue]);
});
</script>
{{{ SCRIPT }}}
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment