🛠 Game building guide
Anyone can build a mini game in a single JS file and publish it here. Canvas and DOM are all yours, and sound works too.
🧪 Upload in the studio →How does it work?
- Build your game as a single JS bundle (2MB or less) and upload it
- Once it passes the automated security scan, only you can preview and test it in the studio
- After a moderator plays it and approves, it shows up in everyone's room creation list
Your game runs inside a sandboxed iframe on a separate origin. The only way it talks to the outside is the window.CimeGame SDK.
What the SDK gives you
Lifecycle callbacks hand you the room, the round and the state; submitAction sends player actions back.
// 게임 번들은 window.CimeGame SDK로만 통신합니다
CimeGame.onInit(({ roomCode, me, seed }) => {
// roomCode: 방 코드 · me.nickname: 내 닉네임
// seed: 모든 플레이어가 공유하는 랜덤 시드 (맵/문제 동기화용)
});
CimeGame.onRoundStart(({ endsAt, config }) => {
// 라운드 시작 — endsAt(ms 타임스탬프)까지 플레이
// 남은 시간 = endsAt - Date.now()
});
CimeGame.onState((state) => {
// 서버가 브로드캐스트하는 게임 상태 (실시간)
});
CimeGame.onRoundEnd((results) => {
// 라운드 종료 — 정리/결과 화면 등
});
// 플레이어 행동은 액션으로만 전송 — 점수 계산은 서버가 합니다
CimeGame.submitAction({ type: "score", score: 42 });onInit(ctx)— Gives you roomCode / me / seed. Every player gets the same seed, so use it to generate maps or questions and every screen stays in synconRoundStart(round)— Gives you endsAt (an end timestamp in ms). Compute the remaining time with endsAt - Date.now()onState(state)— The state the server broadcasts in real timeonRoundEnd(results)— Fires when the round endssubmitAction({type:"score", score})— Reports a score
🔒 The server has the final say on scores — reported scores are validated as monotonically increasing (only a higher score counts) and capped. Inflating a score on the client changes nothing.
Adding sound
Synthesize effects and background music with the Web Audio API. External audio files (.mp3/.wav) are blocked by the CSP default-src 'none', so they can't be loaded.
// 소리는 Web Audio API로 직접 "합성"합니다 (오디오 파일 로드는 CSP로 차단)
let audioCtx;
// ⚠️ AudioContext는 첫 사용자 제스처(클릭/터치/키)에서 생성·resume해야 소리가 납니다
function ensureAudio() {
if (!audioCtx) audioCtx = new (window.AudioContext || window.webkitAudioContext)();
if (audioCtx.state === "suspended") audioCtx.resume();
}
function beep(freq = 440, ms = 120) {
ensureAudio();
const osc = audioCtx.createOscillator();
const gain = audioCtx.createGain();
osc.frequency.value = freq;
osc.connect(gain); gain.connect(audioCtx.destination);
gain.gain.setValueAtTime(0.2, audioCtx.currentTime);
gain.gain.exponentialRampToValueAtTime(0.001, audioCtx.currentTime + ms / 1000);
osc.start();
osc.stop(audioCtx.currentTime + ms / 1000);
}
// 첫 클릭에서 오디오 활성화 + 효과음
document.getElementById("game").addEventListener("click", () => beep(660, 100));⚠️ The AudioContext must be created and resumed inside the first user gesture (click, touch or key press), or nothing will play. Because of autoplay policies and the sandbox, playing right after load is silent. Call resume() on the first input, e.g. a start button.
Design guide
- The iframe is an opaque-origin sandbox — draw freely with canvas and DOM
- External images, fonts and network calls are all blocked by the CSP. Build assets in code — SVG, canvas drawing, inline data: URIs
- Keep pixel art crisp with image-rendering: pixelated
- Scale against window.innerHeight to fit every device height
- Colours and tone are up to you — dress it up however suits your game 🎨
// 화면 맞춤: window.innerHeight 기준으로 스케일 (기기별 높이 대응)
const BASE_H = 640; // 디자인 기준 높이
function fit(el) {
const scale = window.innerHeight / BASE_H;
el.style.transform = `scale(${scale})`;
el.style.transformOrigin = "top center";
}
window.addEventListener("resize", () => fit(stage));
// 픽셀아트는 뭉개짐 방지
// canvas.style.imageRendering = "pixelated";Restrictions (upload scanner)
For safety we statically scan the code on upload. The items below are rejected even when they only appear in a comment, so avoid them entirely.
evalFunctionfetchXHRlocalStoragedocument.cookieWebSocketwindow.top- No dynamic code execution (eval, new Function)
- No external network calls (fetch/XHR/WebSocket) — the CSP blocks them anyway
- No cookie, storage or parent window access (document.cookie, localStorage, window.top)
- Bundle size 2MB or less — inline assets as SVG, canvas or data URIs
Review criteria
- Is it fun to play together with viewers during a stream?
- Can it be played on mobile?
- Is it free of hateful, gambling-like or otherwise inappropriate content?
If we reject it we'll tell you why, and you can fix it and upload again.