Skip to content

Run in the browser

Run the scene from the earlier pages in a browser with the WebGPU backend, behind AwakeKt's loading screen.

WebGPU Web (wasmJs)

The app and GameRenderPlan stay in commonMain unchanged. This page adds a wasmJs target, the WebGPU backend, and a page to host it.

Add the WebGPU backend

The WebGPU backend builds on wgpu4k, which only publishes snapshot builds. So the backend is published as a snapshot too, to Maven Central's snapshot repository, and Core releases do not include it. Each Core release publishes a WebGPU snapshot built from it instead.

Add the snapshot repository in settings.gradle.kts:

settings.gradle.kts
dependencyResolutionManagement {
    repositories {
        mavenCentral()
        maven("https://central.sonatype.com/repository/maven-snapshots/") {
            content { excludeGroup("io.ygdrasil") }
        }
        // The wgpu4k builds the WebGPU backend was tested with, which Sonatype no longer serves.
        maven("https://awakekt.github.io/awake/") { content { includeGroup("io.ygdrasil") } }
    }
}

Add the backend to the catalog at your Core version with -SNAPSHOT appended, the WebGPU build of that Core release:

gradle/libs.versions.toml
[libraries]
awake-backend-webgpu = { module = "com.awakekt.awake.backend:webgpu", version = "0.43.0-SNAPSHOT" }

Then add a browser target and the dependency:

build.gradle.kts
kotlin {
    @OptIn(org.jetbrains.kotlin.gradle.ExperimentalWasmDsl::class)
    wasmJs {
        browser()
        binaries.executable()
    }

    sourceSets {
        wasmJsMain.dependencies {
            implementation(libs.awake.backend.webgpu)
        }
    }
}

The snapshot carries the exact wgpu4k builds it was tested with, so Gradle resolves those without more setup.

Keep WebGPU on your Core release

0.43.0-SNAPSHOT depends on every Core module at exactly 0.43.0 and holds them there, so a build that also asks for a newer Core fails to resolve rather than mixing two, and an older Core in the build is moved up to 0.43.0. Do not take the newest -SNAPSHOT in the snapshot repository instead: that one is built from main after the release, and it moves the wasmJs target onto Core snapshots.

Launch the app

In wasmJsMain, hand the same app and plan to launchWebGpuGame:

wasmJsMain/kotlin/Main.kt
import com.awakekt.awake.webgpu.application.launchWebGpuGame

fun main() = launchWebGpuGame(firstScene(), GameRenderPlan)

launchWebGpuGame finds the <canvas> with id awake-canvas, keeps it the size of the window, and draws a frame on every animation frame. Pass canvasId to use a different canvas.

On a high-DPI screen the canvas has 2.25 to 9 times the pixels of a 1x one. To trade some of them for frame rate, pass renderScale = { settings.renderScale }: the 3D scene renders at that fraction of the canvas and the UI stays sharp. maxPixelRatio caps the whole canvas, UI included. See Resolution and render scale.

Add the page and the loading screen

Put an index.html in src/wasmJsMain/resources/:

src/wasmJsMain/resources/index.html
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
    <title>Hello AwakeKt</title>
    <style>
        html, body { margin: 0; width: 100%; height: 100%; overflow: hidden; }
        canvas { display: block; width: 100vw; height: 100vh; }
    </style>
</head>
<body>
<canvas id="awake-canvas"></canvas>
<script src="awake-loader.js" data-product="Hello"></script>
<script src="hello.js"></script>
</body>
</html>
  • The canvas id matches what launchWebGpuGame looks for.
  • hello.js stands for your app's bundle, which is named after the Gradle module.
  • awake-loader.js is AwakeKt's loading screen. Copy awake-loader.js from the WebGPU backend into src/wasmJsMain/resources/, next to index.html. Load it before the bundle.

The loading screen shows the AwakeKt mark and a progress bar: first the .wasm download, by bytes, then WebGPU and shader startup. The first frame drawn to the canvas dismisses it. If the download fails, the browser has no WebGPU, or the engine fails to start, it shows a message instead of leaving a black canvas. data-product is the name shown after "AwakeKt" on the screen; leave it out to show only "AwakeKt".

For a game rather than an AwakeKt product, set data-mode="game" and data-title="Your Game": the screen turns neutral monochrome, so it suits any art direction, shows your game's title, and keeps a small "Made with AwakeKt" line. Add data-attribution="none" to drop that line.

Ship the texture transcoder

A project's cooked textures (.ktx2) are transcoded in the browser by Basis Universal, which the page loads from basis/basis_transcoder.js and basis/basis_transcoder.wasm beside index.html. Apply Core's Gradle plugin to your web module and it puts them there, for the development server and the production build:

plugins {
    id("com.awakekt.awake.plugin.basis-transcoder") version "<your Core version>"
}

It takes the files from the texture-ktx-loader-wasm-js klib your app already depends on and checks them against the digests Core pins, so the build fails if they aren't the release's own; keep the plugin on the same version as Core. An app that doesn't use KTX2 textures gets nothing added. The game template applies it already.

If you can't apply the plugin, copy the two files by hand from the basis/ folder of the klib of your Core version. The klib is on Maven Central under com.awakekt.awake.asset, and in your Gradle cache once the app has built:

unzip texture-ktx-loader-wasm-js-<version>.klib 'basis/basis_transcoder.js' 'basis/basis_transcoder.wasm' -d src/wasmJsMain/resources

To check, open basis/basis_transcoder.js from the running page: it must be JavaScript, not an HTML 404 page. Without the files the app still runs, logs "No Basis Universal transcoder" once with the path it tried, and loads each texture's mobile file or source image instead.

Ship the socket worker

A game that talks to its server with awake:net:client-browser runs its WebSocket in a Web Worker from awake-net/socket-worker.js. Apply com.awakekt.awake.plugin.socket-worker to your web module the same way and the page serves it; without it the socket runs on the page. See Networking in the browser.

Run it

./gradlew :your-module:wasmJsBrowserDevelopmentRun

Open the address it prints in a browser with WebGPU. The loading screen suggests a current Chrome or Edge, or Safari 26 or later.

One plan for both backends

The WebGPU backend runs as much of GameRenderPlan as it supports and reports anything it leaves out, so the plan never needs a browser copy.

No WebGPU, no start

In a browser without WebGPU, launchWebGpuGame stops before the engine starts, with "WebGPU is unavailable in this browser or device", and the loading screen says so.

See also