Add js/wasm (WebGL2) backend for the opengl package#128
Draft
fisherevans wants to merge 11 commits into
Draft
Conversation
Adds a parallel WebGL2 backend under the `js && wasm` build tag so pixel can target the browser without touching desktop code paths. The existing GLFW + go-gl files are tagged `!js`; new `*_wasm.go` siblings implement Window, Canvas, input (keyboard/mouse via DOM events), and stubs for cursor/joystick/monitor. Also refactors a few desktop call sites off the raw `go-gl/gl` package and onto glhf wrappers (`BlendFuncSeparate`, `BlendEquation`, `ActiveTexture`) so the same source compiles for both targets. Two of pixel's internal shaders gained explicit float literals (`0.0`, `2.0`) so GLSL ES 300 — which is stricter about int->float conversion than 330 core — accepts them unchanged. Depends on companion WASM branches of glhf and mainthread. Desktop behavior is unchanged. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The PR description claimed mouse input was wired through DOM events,
but input_dom_wasm.go only installed keyboard listeners. Add mousedown /
mouseup / mousemove / mouseenter / mouseleave / wheel / contextmenu
handlers, map MouseEvent.button to pixel.MouseButtonN, and fire all
user-registered callbacks (button, char, mouse moved/entered, scroll)
so game code that sets them sees the same events as on desktop.
Also drop the unused internal.InputHandler{} shim and its stale "not
wired up yet" comment, and add a WebAssembly section to the top-level
README describing how to load the canvas, which features are stubbed,
and the required build command. Adjust the "Missing features" list so
the HTML5 backend isn't still called out as missing.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Polls navigator.getGamepads() once per UpdateInput and feeds the results through the same internal.JoystickState machinery the desktop backend uses. Standard-layout pads are remapped so button / axis order matches the GLFW backend (including promoting LT/RT from analog buttons to the trigger axes); non-standard pads pass through as raw indices so applications can still address them.
This was referenced Apr 21, 2026
added 7 commits
April 21, 2026 10:59
On WASM, mainthread.CallNonBlock spawns a goroutine. Every WebGL call via syscall/js is a goroutine scheduling point, so the deferred clear can preempt between draw calls and wipe an in-flight render. Using the blocking Call ensures the clear runs synchronously in order with other GL operations.
On WASM, mainthread.CallNonBlock spawns a goroutine for each GL call. Because every syscall/js invocation is a goroutine scheduling point, a non-blocking draw or vertex upload can be preempted by window.Update (SwapBuffers) before it runs, producing black frames. Changed both canvasTriangles.draw and CopyVertices to use the blocking Call. CopyVertices previously used CallNonBlock only for small batches (<256 floats) as a perf optimization. The optimization is dropped in favor of correctness across all platforms.
Maps touchstart/touchmove/touchend/touchcancel to MouseButton1 + mouse move events so single-finger touch works identically to a left-click on all platforms. preventDefault on all touch events (with passive:false) stops browser pan, zoom, and long-press callout behaviours.
…loading Auto-reload was silent and jarring on mobile (iOS drops WebGL contexts when switching tabs). Now the page handles recovery UX via the callback; falls back to reload if the callback is not defined.
MaxDevicePixelRatio (default 2) prevents DPR=3 devices (iPhone Pro) from allocating a ~12 MB framebuffer that exhausts iOS GPU memory limits. DPR=2 is indistinguishable for pixel-art content. pixelOnContextLost now receives a diagnostics object: renderer, dpr, backing store dimensions, estimated framebuffer size, elapsed time, and loss count so the page can surface actionable info to the user.
…unting Track all active touches by Touch.identifier in Window.activeTouches. ActiveTouches() returns a snapshot of all simultaneous touch positions so callers (e.g. virtual gamepads) can hit-test each finger independently. Fix MouseButton1 edge detection under multi-touch: previously any finger lifting fired Release even if other fingers were still down. Now Press fires only when the count goes 0→1, Release only when it goes 1→0. Desktop Window gets a nil-returning stub so the method is available on both platforms without conditional compilation at call sites.
Contributor
|
Hey, since it looks like you're still working on this, do you mind changing this to draft until you're ready please? |
Author
|
Yeah, of course - my apologies |
Contributor
|
No worries! Thanks for that! |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds a parallel WebGL2 backend to the
openglpackage under thejs && wasmbuild tag so pixel can target the browser. The existing GLFW + go-gl files gain//go:build !js; new*_wasm.gosiblings implement:requestAnimationFrame, handles WebGL context loss by reloading the page, auto-syncs the canvas backing store to CSS size ×devicePixelRatioeach frame so resize/fullscreen transitions propagate intowindow.Bounds().keydown,keyup,mousedown,mouseup,mousemove,wheel,mouseenter,mouseleave,blur,contextmenu) mapped onto pixel'sButtonconstants, fed through the sameinternal.InputHandlerthe desktop backend uses, and surfaced through the existingPressed/JustPressed/MouseScroll/ callback APIs.navigator.getGamepads()). Standard-layout pads are remapped so button and axis order match the desktop GLFW backend (including promoting LT/RT from analog buttons to the trigger axes); non-standard pads pass through as raw indices.mainthread.Runwith a direct call under WASM.The only desktop-visible change is explicit float literals (
0.0,2.0) added to two of pixel's internal shaders — GLSL ES 300 is stricter about int→float conversion than 330 core, so the change lets the same source compile for both targets.Dependencies
Depends on companion WASM branches of glhf and mainthread:
Test plan
go build ./...,go test ./...— twotests/floating-point failures are pre-existing on upstreammain).GOOS=js GOARCH=wasm go build ./...succeeds against the companion glhf + mainthread WASM branches.GOOS=js GOARCH=wasm go test -ccompiles all packages cleanly.