Hi,
I’m trying to get the basic Babylon LITE (v1.26.0) demo/example working within an Android WebView v151.0.7…(sic) (with Jetpack Compose code). It works fine inside the Chrome Browser on my phone, but doesn’t display anything inside the WebView in my App.
I’ve enabled Javascript, etc (as per another bug report) to no avail. I’ve also used Chrome DevTools to see if any problems are reported to the Console, but everything looks ok.. however, nothing is visible from BabylonJS (the rest of the page displays fine)!
For info, all the babylonJs code is bundled into one html file (using Bun) and is loaded and displayed, but BabylonJS doesn’t seem to render anything in the canvas tag! ![]()
Here’s the main code below (any help appreciated!)
@SuppressLint("SetJavaScriptEnabled")
@Composable
fun MyView(context: Context,
initialUrl: String = "https://appassets.androidplatform.net/assets/www/index.html",
modifier: Modifier = Modifier) {
val assetLoader = WebViewAssetLoader.Builder()
.addPathHandler("/assets/", WebViewAssetLoader.AssetsPathHandler(context))
.build();
AndroidView(
modifier = modifier.fillMaxSize(),
factory = { context ->
WebView.setWebContentsDebuggingEnabled(true) // DEBUG TMP - use Chrome DevTools on PC to debug... https://developer.android.com/develop/ui/views/layout/webapps/debug-chrome-devtools
WebView(context).apply {
webViewClient = LocalContentWebViewClient(assetLoader)
settings.javaScriptEnabled = true
settings.allowContentAccess = true
settings.allowFileAccess = true
settings.allowFileAccessFromFileURLs = true
settings.allowUniversalAccessFromFileURLs = true
settings.domStorageEnabled = true
settings.databaseEnabled = true
settings.mediaPlaybackRequiresUserGesture = false
loadUrl(initialUrl)
}
}
)
}
private class LocalContentWebViewClient(private val assetLoader: WebViewAssetLoader) : WebViewClientCompat() {
override fun shouldInterceptRequest(view: WebView, request: WebResourceRequest): WebResourceResponse? {
return assetLoader.shouldInterceptRequest(request.url)
}
@RequiresApi(Build.VERSION_CODES.O)
override fun onRenderProcessGone(view: WebView?, detail: RenderProcessGoneDetail?): Boolean {
// 1. Log or track the cause of the crash
val didCrash = detail?.didCrash() ?: false
Log.e("WebViewClient", "WebView renderer process gone. Did crash: $didCrash")
view?.let { // 2. Clean up the dead WebView instance
it.destroy() // Remove it from its layout parent if necessary
}
// 3. Trigger your custom UI update (e.g., show a reload button) - see below...
//onCrashHandled()
// 4. Return true to stop the Android app from crashing
return true
}
}
Chrome DevTools Web Console output:
WebGPU is supported on this Android device!
index.html:650 Hello world!
/favicon.ico:1 Failed to load resource: net::ERR_CACHE_MISS
index.html:36 Babylon Lite v1.26.0 - WebGPU engine
index.ts
import { init, destroy } from "./mine";
window.onload = init;
window.addEventListener('pagehide', (event) => {
// If the event's persisted property is `true` the page is about // to enter the back/forward cache, which is also in the frozen state.
// If the event's persisted property is not `true` the page is // about to be unloaded.
const is_dead = !(event.persisted)
if (is_dead) { destroy() }
});
mine.ts
import {
createEngine,
startEngine,
destroyEngine,
createScene,
addToScene,
registerScene,
loadEnvironment,
loadGltf,
createSceneContext,
createDefaultCamera,
createFreeCamera,
createArcRotateCamera,
attachControl,
createHemisphericLight,
createGround,
createSphere,
createPbrMaterial,
startRenderLoop,
} from "@babylonjs/lite";
let g_engine: any = null; // Keep a global tracking variable outside the init block to handle re-initialisation safely
let g_scene: any = null; // Keep a global tracking variable outside the init block to handle re-initialisation safely
export async function init() {
if (navigator.gpu) {
console.log("WebGPU is supported on this Android device!");
} else {
console.log("ERROR: WebGPU is not supported in this WebView! Falling back to WebGL.");
alert("ERROR: WebGPU is not supported in this WebView! (Falling back to WebGL?)");
}
init2().catch( (e) => { console.error(e); alert("ERROR: "+e); } )
}
async function init2() {
console.log("Hello world!");
alert("MINE ON!");
// setup ---------------------
const canvas = document.getElementById("renderCanvas") as HTMLCanvasElement;
if (g_engine) { await g_engine.dispose(); } // CRITICAL MEMORY FIX: If an engine instance exists from a previous run, destroy it completely
g_engine = await createEngine(canvas); // Save reference to prevent leaks on reload
if (g_scene) { await g_scene.dispose(); }
g_scene = createSceneContext(g_engine);
// create ---------------------
test(canvas, g_engine, g_scene);
// run ---------------------
await registerScene(g_scene);
await startEngine(g_engine);
// final setup-------------
window.addEventListener("resize", () => {
g_engine.resize();
});
}
export function destroy() {
if (g_scene != null) { disposeScene(g_scene); } // releases all meshes, renderables, disposables
if (g_engine != null) { disposeEngine(g_engine); } // destroys GPU device, render targets, swapchain
}
async function test(canvas, engine, scene) {
const camera = createArcRotateCamera(-Math.PI / 2, Math.PI / 2.5, 4, { x: 0, y: 0, z: 0 });
scene.camera = camera;
attachControl(camera, canvas, scene);
addToScene(scene, createHemisphericLight([0, 1, 0], 1.0));
const sphere = createSphere(engine, { segments: 16, diameter: 2 });
sphere.material = createPbrMaterial({ baseColorFactor: [0.9, 0.1, 0.1, 1], metallicFactor: 0.1, roughnessFactor: 0.4 });
addToScene(scene, sphere);
}
Sorry if this code looks horrible: I don’t really know what I’m doing! ![]()
The html:
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<title>Mine On!</title>
<style>
body {
min-height: 100vh;
margin: 0;
padding: 0;
}
canvas {
max-width: 100vw;
max-height: 100vh;
width: 100vw;
height: 100vh;
touch-action: none;
}
</style>
<script src="./index.ts"></script>
</head>
<body>
<h1>Help Me!</h1>
<p>
Hello
</p>
<hr>
<canvas id="renderCanvas"></canvas>
<hr>
<p>
.. canvas is above here..
</p>
<img src="./brdf-lut.png">
</body>
</html>