Porting jMonkeyEngine to the Browser with WebGL2 and TeaVM (sourcecode released)

With the advancements in Open GLES support in browsers using WebGL2 and the progress in transpiling Java to JavaScript with TeaVM, I have begun to explore the possibility of porting jMonkeyEngine to the browser.

After a week of dedicated work, I am excited to present the initial prototype of what I hope will evolve into a module and plugin for the standard jME build system. At this stage, since it requires ongoing patches to the engine that are pending in my pull requests, the best I can offer is a link to a forked version of jMonkeyEngine that includes all necessary patches and introduces the new jme3-web module.


Status Description
rendering OK Ported to WebGL 2 (GL ES 3)
audio OK Ported to WebAudio (limited environment support (the predefined environments) via public domain impulses from http://reverbjs.org/)
mouse input OK
keyboard input OK
touch input OK
joystick input NOT SUPPORTED
Module Status Description
jme3-core PARTIAL
jme3-plugin PARTIAL
jme3-effects OK
jme3-jogg OK
jme3-terrain OK
jme3-jbullet OK ported to Ammo.js (bullet emscripten build)
jme3-testdata PARTIAL some assets are not supported (See Supported Assets )
jme3-niftygui ???
jme3-awt-dialogs NOT SUPPORTED Needs to be ported to html
jme3-networks NOT SUPPORTED Needs websocket connector

Supported Assets

The current implementation offers support for a subset of available asset loaders. For a complete list, refer to jme3-web/src/main/resources/Web.cfg. If you wish to add more asset loaders along with their supported extensions, edit Web.cfg and make sure the teavm optimization preserves their classes by adding them to classesToPreserve[] in jme3-web/build.gradle.

Getting Started


Testing, developing, or porting applications to this fork, while slightly cumbersome, is relatively straightforward.

To begin, clone the source code repository.

Once you’ve cloned the repository and opened it in your preferred code editor, navigate to jme3-web/src/main/java/com/jme3/web/WebApp.java. This file contains the main() method, which serves as the entry point. Create and initiate your simple application from this point, as you would typically.

For any reflection usage outside the standard jME practices (already covered), include the relevant classes in the whitelist within jme3-web/src/main/java/com/jme3/web/jvm/TeaReflectionSupplier.java.
This step ensures that TeaVM generates the necessary supporting methods.

If your application requires additional assets, place them in the jme3-web/src/main/resources directory.

Resource Prefetching

Given that all resources are fetched via HTTP, loading assets comes with added latency. If your application depends on loading substantial resources on demand, consider downloading them automatically before launching the app. While this feature is supported by jme3-web, it’s disabled by default.

To enable it, specify one or more regex patterns in jme3-web/src/main/resources/resourcesPrefetchFilters.txt (one per line) to match the paths of resources you wish to prefetch (e.g., .+ to prefetch all resources). Before the application starts, the resource loader will download the matched resources, which will be stored in the browser cache using the virtual filesystem API. Note that this action only downloads resources and doesn’t load them into VRAM. To load them into VRAM, use assetManager.preload() as usual.


Once everything is set up, compile using the following commands:

./gradlew --no-daemon compileJava generateJavaScript copyAllResources generateResourcesIndex
(Note: In my testing, omitting --no-daemon led to partially cached builds and improper compilation.)


After compilation, you’ll find the generated JavaScript and copied resources in jme3-web/build/generated/teavm/js/.

Keep in mind that some HTML5 APIs require a secure context. To test your application, host the files on an HTTPS server or a local server bound to localhost.

For Linux users, you can run:

busybox httpd -f -p8080 -h jme3-web/build/generated/teavm/js/

and then connect to http://localhost:8080 with your favorite browser.


The latest demo (11):



  • preload renamed to prefetch ( resourcesPreloadFilters → resourcesPrefetchFilters) for more clarity
  • updated demo


Great way to modernize jme!

1 Like

This is fantastic work.

Great news! thank you!

This is so COOOOOL.
We now have a ScriptMonkey!!!

Great job, thank you!

Tried running the PBR demo, but not sure if the result is correct.

Oh, i’ve noticed the same artifacts when trying to load the web demo on mobile.
I think it’s a precision issue somewhere, what are your device’s specs? And browser?

A-WE-SO-ME!!! I would love to test all this when I got some time…

Thanks for the hard work

Linux mint 20.1
Firefox 112.02
AMD/ATI Robson CE Radeon HD 6370M
Mesa 21.2.6
GLSL Version: 3.30
OpenGL Version: 3.3

It took a while but i managed to isolate the issue… it seems it affects android too…
Basically the RGB8 format was mapped to RGB565 internally… and it seems that some drivers silently remap it to an higher precision making it not noticeable on some platforms (eg. my desktop and laptop) but others don’t like my phone and your device…

See this line:

GLImageFormats is a big mess, we should rewrite it entirely at some point, but for now i think i’ve patched it.

Please check it out here: https://test-jme-web10.rblb.it


Yep, looks much better now, thanks!

By the way, would be nice to increase the metallic factor a bit, you know, to have a better feel of it is PBR! :slightly_smiling_face:


Just in case, could this perhaps be of any help?

1 Like

PBR and Physic are fine.
Shadow looks abnormal
Windows 11
AMD AMD Ryzen 5 3550H with Mobile Vega
Browser: Yandex 23.7

1 Like

I’ve updated the demo and repo https://test-jme-web11.rblb.it/

Yes, teavm already comes with websocket bindings so we are good on the clientside, but we need a java library for the serverside code (that supposedly would still run on java) . I’ve found this library that seems ok GitHub - TooTallNate/Java-WebSocket: A barebones WebSocket client and server implementation written in 100% Java. .

Can you try the new demo?


I tried to build the main project but I am getting an error (see below)

What I did:

  1. Cloned the repo
  2. opened in IntelliJ.
  3. From command line, ran the suggested gradle command from the root of the git project
    gradlew.bat --no-daemon compileJava generateJavaScript copyAllResources generateResourcesIndex

I am using Windows. Could this be a path separator issue? (just from looking at the error message)

FWIW the file jme3-web\build\classes\java\main\com\jme3\web\context\HeapAllocator.class does exist…

> Task :jme3-web:generateJavaScript
Enable reflection for com.jme3.export.Savable
Enable reflection for com.jme3.asset.cache.AssetCache
Enable reflection for com.jme3.asset.AssetLoader
Enable reflection for com.jme3.asset.AssetLocator
Enable reflection for com.jme3.audio.Filter
Enable reflection for com.jme3.anim.util.JointModelTransform
Enable reflection for com.jme3.material.logic.TechniqueDefLogic
Enable reflection for com.jme3.util.clone.JmeCloneable
Enable reflection for com.jme3.system.JmeSystem
Enable reflection for com.jme3.system.JmeSystemDelegate
Enable reflection for com.jme3.asset.cache.SimpleAssetCache
Enable reflection for com.jme3.texture.TextureProcessor
Enable reflection for com.jme3.asset.CloneableAssetProcessor
Enable reflection for com.jme3.util.SafeArrayList
Enable reflection for com.jme3.web.context.HeapAllocator
Enable reflection for com.jme3.json.TeaJSONParser
Enable reflection for com.jme3.web.filesystem.WebResourceLoaderImpl
Enable reflection for com.jme3.web.filesystem.WebResourceLoaderImplNoCache
java.lang.Class patched
java.util.logging.Logger patched
java.nio.ByteBuffer patched
java.lang.ThreadLocal patched
java.nio.ByteOrder patched
java.lang.ref.ReferenceQueue patched
java.lang.ClassLoader patched

> Task :jme3-web:generateJavaScript FAILED

FAILURE: Build failed with an exception.

* What went wrong:
Execution failed for task ':jme3-web:generateJavaScript'.
> org.teavm.tooling.builder.BuildException: java.lang.NoClassDefFoundError: com/jme3/web/context/HeapAllocator (wrong name: com\jme3\web\context\HeapAllocator)
1 Like

Maybe a path separator issue here:

or here

Can you try to replace this line

def filePath = file.path.replaceAll('\\.java$', '').replaceAll('/', '.').substring(mainJavaDir.path.length() + 1)


def filePath = file.path.replaceAll('\\.java$', '').replaceAll('/', '.').replace("\\",".").substring(mainJavaDir.path.length() + 1)

and see if it works?

1 Like

yes that worked. After that I got the demo to run. Thank you.

1 Like

jME embeddable web scene viewer with PBR:

Would it make sense to have something like that embeddable in the hub? Like this:


Uuh nice. Really cool. Now we can have a proper asset store :slight_smile: Guaranteed results, WYSIWYG.