본문으로 건너뛰기
cccskills
무료GitHub에 공개

melonjs-renderer-backends

Use this skill to understand why a melonJS feature behaves differently across machines and renderer backends. It explains the WebGPU, WebGL 2, and Canvas fallback order, capability checks, graceful degradation, backend pinning, and handling GPU context restoration.

한국어 요약은 준비 중입니다. 영어 요약을 표시하고 있습니다.

원문원문 설명 보기

Use this skill when a feature works on one machine and not another, when choosing or pinning a renderer, or when writing code that must degrade gracefully. Covers the WebGPU to WebGL 2 to Canvas fallback ladder, what each backend supports, capability detection, and the subsystems that warn once and then stop working on Canvas. Triggers on: video.AUTO, video.WEBGPU, video.WEBGL, video.CANVAS, renderer.type, supportsDepthBuffer, shaderLanguage, WebGPU, WebGL2, Canvas fallback, context loss, backend, works on my machine.

melonjs/melonJS스타 6,429(GitHub 6,429 + cccskills 0)

실행 점수 미검증

설치 방법 보기

이럴 때 유용합니다

  • Diagnosing a visual feature that fails on some devices
  • Checking renderer capabilities before enabling effects
  • Choosing whether to pin a renderer for a specific reason
  • Reacquiring GPU resources after context restoration

포함된 파일(1)

  • SKILL.md9.4 KB

SKILL.md(원문)

설치하기 전에 에이전트에게 전달되는 지시 내용을 확인할 수 있습니다.

Renderer backends and graceful degradation

video.AUTO picks a backend at runtime. The same code runs on a different renderer depending on the machine, and several subsystems degrade to nothing rather than erroring. This is the root cause of most "works on my machine" reports.

The ladder

video.AUTO (the default) tries WebGPU → WebGL 2 → Canvas and uses the first that initialises.

const app = new Application(800, 600, {
    parent: "screen",
    renderer: video.AUTO,     // or video.WEBGPU / video.WEBGL / video.CANVAS
});
await app.init();

WebGL 1 is not in the ladder — 20.x made WebGL 2 the baseline, and pinning video.WEBGL rejects on a WebGL-1-only device rather than degrading.

What each backend can do

WebGPUWebGL 2Canvas
sprites, tilemaps, text✅✅✅
blend modes✅✅✅ except "none"
shaders and post effects✅✅❌ inert
Light2d glow (drawLight)✅✅✅ cached radial gradient
normal-map lighting, Light3d✅✅❌ inert
Mesh✅✅⚠️ CPU-projected, no depth
Camera3d / depth sorting✅✅❌ warns, renders wrong
InstancedMesh✅✅⚠️ one draw per instance
GPU tilemap rendering✅✅❌ per-tile CPU path

The Canvas column is the problem: none of it throws. Each of those paths warns once and then degrades or no-ops, so the game runs and looks wrong.

Two Canvas rows are worth spelling out because they are easy to misread as total failures. A Light2d still draws — the Canvas renderer bakes a radial gradient per light and blits it — what disappears is per-pixel normal-map shading, which warns once (Light2d normal-map lighting requires the WebGL renderer) and then renders sprites unlit. A Mesh still draws too, sorted with the painter's algorithm and no depth buffer, so convex models look right and anything self-overlapping does not. "none" (replace, alpha included) is the one blend mode Canvas cannot express — there is no globalCompositeOperation that disables blending for the drawn area alone — so it resolves to "normal", and setBlendMode reports that back.

Detecting what you have

app.renderer.type;                    // "WebGPU" | "WebGL2" | "CANVAS"
app.renderer.supportsDepthBuffer;     // the practical "is this a GPU backend" test
app.renderer.shaderLanguage;          // "wgsl" | "glsl" | null (null on Canvas)

There are narrower flags for the features that do not travel together: supportsShaderTileLayers, supportsRetainedMesh and supportsInstancing — all three false on Canvas, true on both GPU backends. Prefer a capability flag over comparing type against a string; type is an identity check for code coupled to one backend's machinery.

For a scene that genuinely requires a GPU backend, fail loudly rather than rendering something broken:

await app.init();
if (!app.renderer.supportsDepthBuffer) {
    throw new Error("this game needs WebGL 2 or WebGPU");
}

Writing code that degrades well

Decorative effects — let them no-op. A missing vignette is fine.

Load-bearing effects — guard the carrier. If a shader draws your content rather than decorating it, the carrier renderable shows through raw when the effect is disabled: a 1×1 white texture stretched to fill a quad renders as a white box exactly where the effect should have been.

if (effect.enabled) {
    // draw the carrier the shader consumes
}

Dual-language shaders — a ShaderEffect compiles the body matching renderer.shaderLanguage. With no matching body — a GLSL-only effect on WebGPU, any effect on Canvas — the constructor warns once, leaves enabled === false, and every method becomes a no-op. Pass { glsl, wgsl } rather than a bare string, or gate on renderer.shaderLanguage.

Pinning a backend

Pin only with a reason. video.WEBGL costs you the WebGPU path and the Canvas fallback, so a WebGL-1-only device gets nothing at all.

Legitimate reasons: reproducing a backend-specific bug, a benchmark, or a known driver issue in the field.

For one-off testing you do not need to touch the settings at all: the URI fragments #webgpu, #webgl (or #webgl2) and #canvas override the renderer setting for that run, and behave exactly like passing the matching constant — #webgl on a WebGL-1-only device rejects rather than falling back.

Context loss

A GPU context can be lost at any time — tab backgrounding, driver reset, memory pressure. melonJS handles restoration on both GPU backends: WebGL recompiles every live GLShader and replays its uniform snapshot, WebGPU renegotiates a device and rebuilds its batchers, and both then emit event.ONCONTEXT_RESTORED (the paired loss event is event.ONCONTEXT_LOST).

What you must handle is your own GPU-side state. If you cached a raw texture or program handle, re-acquire it on restore rather than holding the stale one:

import { event } from "melonjs";

event.on(event.ONCONTEXT_RESTORED, (renderer) => {
    // re-acquire anything you were holding by raw handle
});

Test on the backend your users get, not the one your harness picks

video.AUTO prefers WebGPU where it exists. Headless Chromium usually has no GPU adapter and falls back to WebGL, so an automated suite can pass every run while the WebGPU path is broken — the two differ in real, throwing ways (see below). Pin the backend explicitly when it matters: renderer: video.WEBGPU, or the #webgpu / #webgl URI fragment on a page you can open by hand.

Practical differences

  • Frame capture orientation differs: row 0 of a toFrameTexture() capture is the bottom of the frame under WebGL and the top under WebGPU, so a GLSL body that flips with 1.0 - uv.y must not do so in its WGSL twin. The WebGPU capture also preserves alpha, where the WebGL one captures into an opaque RGB texture.
  • The advanced blend modes cost far more than they look. overlay, difference, hard-light, soft-light, color-dodge, color-burn, darken and lighten cannot be expressed as fixed-function blending, so on both GPU backends each such draw is bracketed individually: capture the destination, redraw offscreen, composite through BlendEffect. Fine for accents, ruinous for hundreds of objects. normal, additive, multiply, screen and exclusion are ordinary fixed-function state and cost nothing extra.
  • WebGPU rejects a zero-sized image source; WebGL shrugs at it. Uploading a 0-width or 0-height canvas throws OperationError: GPUQueue.copyExternalImageToTexture: External image state is not valid, from a stack that reads drawImage → addQuad → getBinding. WebGL uploads the same source without complaint, so this is a class of bug that cannot appear on one backend and stops the game dead on the other. The usual source is a Text whose string is empty or whitespace-only — it measures to nothing, so its glyph canvas has no width. Give a placeholder label a real glyph and hide it with alpha/position instead of blanking its text.
  • 3D meshes do not honour blend modes. The mesh pass draws with its own depth/blend state; asking for one of the advanced modes logs a one-time blend mode "…" is not supported for 3D meshes warning and falls back to "normal". Gradient fills warn the same way.

Symptom → cause

symptomcause
effect works locally, not for a userthey got the Canvas fallback, or a different GPU backend
sprites render unlit though lights are in the sceneCanvas fallback — normal-map shading is inert (check the console)
mesh looks inside-out where it overlaps itselfCanvas fallback — painter's algorithm, no depth buffer
shader works on one machine onlyGLSL-only effect, AUTO chose WebGPU elsewhere — the console carries the warning
a solid box where an effect should becarrier drawn while the effect is disabled
nothing renders at all on an old devicevideo.WEBGL pinned; WebGL 1 is rejected, not downgraded
ported shader is vertically flippedthe WebGL capture is bottom-up, the WebGPU one top-down
copyExternalImageToTexture: External image state is not valida zero-sized upload source — most often a Text with an empty or whitespace-only string
runs on the test machine, dies on the dev'sthe harness fell back to WebGL and never exercised WebGPU
a blended mode is ignored on a meshmeshes fall back to "normal"; look for the one-time warning
textures vanish after the tab is restoredcached raw GPU handles not re-acquired on context restore

Related skills

  • melonjs-getting-started — renderer selection in the Application settings
  • melonjs-effects-and-shaders — dual-language shaders and the Canvas no-op
  • melonjs-3d — the tier that requires a GPU backend outright

리뷰

아직 리뷰가 없습니다. 사용해 본 소감을 남겨 주세요.

댓글

로그인 없이 쓸 수 있습니다. 사용 후기, 질문, 작동하지 않은 환경 등을 편하게 남겨 주세요.

아직 댓글이 없습니다. 첫 댓글을 남겨 보세요.

보내면 바로 공개됩니다. 이메일 주소나 전화번호 같은 개인 정보는 쓰지 마세요. URL은 2개까지입니다.

0 / 1,000자

이 양식은 reCAPTCHA로 보호되며 Google 개인정보처리방침 및 서비스 약관이 적용됩니다.

같은 저장소의 스킬

melonjs

무료

Use this entry point to find the melonJS guide for a game development task, from application setup and scenes to sprites, input, physics, audio, and 3D. It also points to companion packages and the API index when the guides do not cover a need.

한국어 요약은 준비 중입니다. 영어 요약을 표시하고 있습니다.

  • Choose a guide for a melonJS game task
  • Find the right setup steps for a new game
  • Locate guidance for a 3D scene or imported model
melonjs/melonJS6.4천2026년 10월 29일 업데이트

Update older melonJS code for version 20.x. Review the asynchronous Application setup, renderer changes, post-effect API, and deprecated exports so you can identify outdated patterns when migrating or debugging expected APIs.

한국어 요약은 준비 중입니다. 영어 요약을 표시하고 있습니다.

  • Migrating a game from melonJS 19.x
  • Checking why an older API is missing
  • Replacing deprecated shader assignments
melonjs/melonJS6.4천2026년 10월 29일 업데이트

Build 3D or 2.5D scenes in melonJS with cameras, meshes, billboards, lights, shadows, and depth sorting. This guide explains the engine’s Y-down and +Z-forward coordinates, camera setup, transparency, and fog, and identifies features that require a GPU renderer instead of the Canvas fallback.

한국어 요약은 준비 중입니다. 영어 요약을 표시하고 있습니다.

  • Setting up a Camera3d for a game stage
  • Placing meshes, billboards, or repeated instances
  • Fixing depth order or transparency in a 3D scene
melonjs/melonJS6.4천2026년 10월 29일 업데이트

Load glTF, GLB, or OBJ/MTL assets into melonJS and understand how their materials, lights, animations, shadows, and instances are handled. The guide explains loader options and common format limitations so you can configure scenes and diagnose missing or incorrectly rendered asset features.

한국어 요약은 준비 중입니다. 영어 요약을 표시하고 있습니다.

  • Loading a glTF or GLB scene into a melonJS level
  • Adding a static OBJ model with its MTL material
  • Checking why imported materials or lights look different
melonjs/melonJS6.4천2026년 10월 29일 업데이트

Manage sound in melonJS games: load audio, play effects and looping background tracks, control volume, and place sounds in the game world. It also covers generated tones and noise, whole-mix effects, and common causes of silent playback.

한국어 요약은 준비 중입니다. 영어 요약을 표시하고 있습니다.

  • Loading sound files and choosing supported audio formats
  • Playing sound effects and looping background music
  • Positioning sounds around a player or game object
melonjs/melonJS6.4천2026년 10월 29일 업데이트

Use this skill to control melonJS cameras and draw shapes, lines, and gradients. It covers target following, camera bounds and effects, coordinate conversion, clipping, rendering into a canvas target, and reading rendered pixels for images or data.

한국어 요약은 준비 중입니다. 영어 요약을 표시하고 있습니다.

  • Making a camera follow a player within world bounds
  • Converting pointer or 3D positions to screen coordinates
  • Drawing shapes or gradients in a custom draw method
melonjs/melonJS6.4천2026년 10월 29일 업데이트

melonjs의 스킬 모두 보기

이 스킬의 문제 신고