Panda3D WebGL Port!


WOW! I’m also very interested in this port, too! Nice! :smiley:


Currently the Emscripten “test” HTML page is unattractive and doesn’t provide a way for the user to see the download progress of the .js file. A simple ‘loading’ screen that shows a progress meter while resources are downloading (similar to the plugin’s) would be useful.

The only problem is that dependencies such as models, textures, etc. are downloaded after the .js file begins running. And, as far as I know, Emscripten doesn’t provide a way for us to monitor the progress of that. (If you look at the code of the test page, there appears to be a sort of progress/loading indication for these, but the actual compiled script never actually calls those functions.)


Of course. There would eventually be a proper replacement for the default emscripten test page, but it’s probably more important to focus on getting all of the core functionality working well at this stage.


While compiling CPython to ASM.js does work, I’m not convinced that it’s the most optimal solution - we’re essentially running a VM inside of a VM, and the CPython VM is really big. I’ve been looking around for Python-to-JS transpilers that will help make a lighter and faster Panda build.

The most promising one is I found is Brython, which seems in active development, and has excellent interoperability with JavaScript - you can directly call JavaScript functions from Python, and Python objects are really just decorated JavaScript objects.

So I dusted off interrogate’s C binding generator, and created an FFI tool to use emscripten’s FFI to spit out JavaScript bindings for Panda3D. Now I can call Panda3D functions from JavaScript and therefore Brython:

Brython tries to be a drop-in replacement for JavaScript, so you’re even able to put this in an HTML page:

<script type="text/python">
from panda3d.core import *

Of course, we would provide a code path to compile to obfuscated JS directly for production use.

There’s still some way to go (overloaded functions are not yet handled, and keyword arguments, etc.), but I can call most Panda classes and functions just fine. It’s promising so far that it compiles and loads so quickly.


Awesome news rdb :smiley:

Thank-you sooo very much for continuing to work on this :smiley:

I know that it is very much appreciated by the community :smiley:

Hopefully soon we can be able to add a simple build step to export to HTML5 :smiley:


Very fancy! :smiley:


Just a note - this port doesn’t work if the browser only has experimental WebGL support.
The application will only run if the official ‘webgl’ support is reported, and not with ‘experimental-webgl’. In the experimental case, there are no errors, but the canvas remains blank (no graphics are rendered).


That’s a bit surprising, as the emscripten function we call does seem to fall back to experimental-webgl: … gl.js#L649


Update: brython seems to be a no-go, since it doesn’t support reference counting, which we need in order to free objects from the emscripten heap, and I can’t spare the time to add this feature.

The only approach that is actually proven to work so far is emscripten-compiled CPython, so I’m back to that. The Python code is frozen using pfreeze and then compiled using emscripten, with a custom main function and loader. It works well, actually, and with a lot of work I managed to make the process fairly efficient, though it’s still a huge amount of code.

It may be interesting in the future to consider Nuitka, which is a compiler that compiles CPython bytecode to actual C code (rather than embedding Python bytecode, although it still depends on CPython). It seems to work well with Panda code on the desktop at least. It may (or may not) offer performance and size benefits. To be investigated.

In any case, I’ve done further work on the WebGL port, making it more robust and adding more features. Here’s another demo showing off pointerlock mode to achieve FPS-style controls, using a modified version of cn04’s loader with tobspr’s design - written in Python!
(Don’t mind the crappy graphics or the sloppy controls - there’s still a lot of things to fix, I mainly slapped this together to test pointerlock and the load process.)

For the early adopters I’ve updated the build dependencies (includes CPython patch). Let me know if you want the front-end that I used to produce the online demos.


Awesome work rdb :smiley:

I tried the demo and it seems to be pretty smooth. :slight_smile:

The loading process and pointerlock worked fine on Firefox 44 , very slick experience like Unity :slight_smile:

How much work do you think remains to get WebGL as valid deployment target ? I would like to use Panda instead of Unity, but I don’t know if Panda is " ready " just yet. :frowning:

I tried downloading the compressed archive but for me at least it seems corrupted ? :frowning:


impressive RDB




Nice work rdb :slight_smile:

I updated the code in my repository on Github to reflect the new style. It also now has a ‘continuous’ loading bar that allocates half of the bar for downloading the application file, and the other half for downloading the additional data. This way the progress bar won’t reset itself in the middle of the loading progress. You can try it out at

I also replaced the String startsWith() with an alternate implementation using indexOf() because startsWith is not suppoted in some browsers such as Chrome 39. Otherwise it will crash with the “Exception thrown, see JavaScript console” error every time.


Also, I noticed that on any browser I tried that only supports experimental-webgl, the Emscripten application always hangs at the “Running…” state. I’m not sure why; no errors are displayed in the console. (The demo at will attempt to run the application with experimental-webgl instead of webgl if it exists.)


Status update:

  • Keyboard support greatly improved. Many missing keys added, keystroke events now work, as well as raw key support. International character input works, but dead keys work only on Chrome (due to a shortcoming in Firefox).
  • Audio works, though .ogg, .wav and .flac files can be loaded only.
  • Egg files and all major image formats except libtiff can be loaded directly, as well as .pz and .gz compressed files - no need to convert to .bam/.txo first.
  • Bullet physics works, or so I’ve been told.

Demonstration of OpenAL with ogg/jpg/egg.pz loading:


Inspired by pmpp’s efforts as well as this, I decided to whip together a live Panda3D code editor. Here it is!

I don’t know about you guys, but I think this is pretty cool. :slight_smile:


I use vs2008 to compile it ,failed :

1>Version: 1.10.0
1>Using DirectX SDK Aug 2009
1>Target arch: x86
1>WARNING: em++ -E -print-search-dirs failed
1>WARNING: em++ -x c++ -v -E /dev/null failed or did not produce the expected result
1>Generating dependencies…
1>[ 0%] Building C++ object embuilt/tmp/p3dtoolbase_composite1.bc
1>Storing dependency cache.
1>Elapsed Time: 1 sec
1>Cannot find em++ on search path
1>Build terminated.


it does not work on Microsoft edge (windows 10)
[] is ok on Microsoft edge


You have to build using emscripten, which is the only thing that will build for asm.js. I also recommend building from a Linux system since I never tested it on Windows. (On a sidenote, we no longer support MSVC 2008).

I only tested it with the major browsers (Chrome and Firefox), so I simply hadn’t bothered to try Edge. It might be trivial to fix, assuming that Microsoft implemented the WebGL standards well enough.


I build web-gl mode in ubuntu,and test it

#include "pandaFramework.h"
#include "pandaSystem.h"
int main(int argc, char *argv[]) {
    // Load the window and set its title.
    PandaFramework framework;
    framework.open_framework(argc, argv);
    framework.set_window_title("My Panda3D Window");
    WindowFramework *window = framework.open_window();
    // Load the environment model.
    NodePath scene = window->load_model(framework.get_models(), "environment");
    // Reparent the model to render.
    // Apply scale and position transforms to the model.
    scene.set_scale(0.25f, 0.25f, 0.25f);
    scene.set_pos(-8, 42, 0);
    // Run the engine.
    // Shut down the engine when done.
    return 0;

make file


PRELOAD_FILES=environment.egg.pz maps/envir-bamboo.png maps/envir-cylinder.png maps/envir-ground.jpg maps/envir-groundcover1.png maps/envir-mountain1.png maps/envir-reeds.png maps/envir-rock1.jpg maps/envir-rock2.jpg maps/envir-tree1.png maps/envir-tree2.png maps/envir-treetrunk.jpg maps/envir-mountain2.png
PANDA_LIBS=libp3framework.bc libpanda.bc libpandaexpress.bc libp3dtool.bc libp3dtoolconfig.bc libp3pystub.a libp3webgldisplay.bc libpandaegg.bc

CFLAGS=-O3 --profiling -s ASSERTIONS=1 -s NO_EXIT_RUNTIME=1 -s TOTAL_MEMORY=32000000

all: pview-panda.html

pview-panda.html: pview-panda.cxx $(PRELOAD_FILES)
	em++ -o pview-panda.html -L$(BUILT)/lib -L$(BUILT)/tmp pview-panda.cxx $(addprefix $(BUILT)/lib/,$(PANDA_LIBS)) -I$(BUILT)/include/ $(CFLAGS) $(addprefix --preload-file ,$(PRELOAD_FILES))

	rm -f pview-panda.html pview-panda.js pview-panda.html.mem

.PHONY: all clean

when i load the page,it download,and show blank,not the scene
the error msg

lzc@lzc-dell:~$ cd /Data/panda3d/panda3d/htmlsample
lzc@lzc-dell:/Data/panda3d/panda3d/htmlsample$ source /Data/emsdk_portable/
Adding directories to PATH:
PATH += /Data/emsdk_portable
PATH += /Data/emsdk_portable/clang/fastcomp/build_master_64/bin
PATH += /Data/emsdk_portable/emscripten/master

Setting environment variables:
EM_CONFIG = /home/lzc/.emscripten
EMSCRIPTEN = /Data/emsdk_portable/emscripten/master

lzc@lzc-dell:/Data/panda3d/panda3d/htmlsample$ export CXXFLAGS="-std=c++11"
lzc@lzc-dell:/Data/panda3d/panda3d/htmlsample$ make -f
make: Nothing to be done for 'all'.
lzc@lzc-dell:/Data/panda3d/panda3d/htmlsample$ make -f
em++ -o pview-panda.html -L../embuilt/lib -L../embuilt/tmp pview-panda.cxx ../embuilt/lib/libp3framework.bc ../embuilt/lib/libpanda.bc ../embuilt/lib/libpandaexpress.bc ../embuilt/lib/libp3dtool.bc ../embuilt/lib/libp3dtoolconfig.bc ../embuilt/lib/libp3pystub.a ../embuilt/lib/libp3webgldisplay.bc ../embuilt/lib/libpandaegg.bc -I../embuilt/include/ -O3 --profiling -s ASSERTIONS=1 -s NO_EXIT_RUNTIME=1 -s TOTAL_MEMORY=32000000 --preload-file environment.egg.pz --preload-file maps/envir-bamboo.png --preload-file maps/envir-cylinder.png --preload-file maps/envir-ground.jpg --preload-file maps/envir-groundcover1.png --preload-file maps/envir-mountain1.png --preload-file maps/envir-reeds.png --preload-file maps/envir-rock1.jpg --preload-file maps/envir-rock2.jpg --preload-file maps/envir-tree1.png --preload-file maps/envir-tree2.png --preload-file maps/envir-treetrunk.jpg --preload-file maps/envir-mountain2.png
emcc: warning: cannot represent a NaN literal '0xbf63010' with custom bit pattern in NaN-canonicalizing JS engines (e.g. Firefox and Safari) without erasing bits!
emcc: warning: cannot represent a NaN literal '0xbf63010' with custom bit pattern in NaN-canonicalizing JS engines (e.g. Firefox and Safari) without erasing bits!
warning: unresolved symbol: posix_spawn_file_actions_init
warning: unresolved symbol: posix_spawn_file_actions_destroy
warning: unresolved symbol: posix_spawn_file_actions_adddup2
warning: Output contains some very large functions (7312 lines in __Z10eggyyparsev), consider building source files with -Os or -Oz, and/or trying OUTLINING_LIMIT to break them up (see settings.js; note that the parameter there affects AST nodes, while we measure lines here, so the two may not match up)
lzc@lzc-dell:/Data/panda3d/panda3d/htmlsample$ python -m SimpleHTTPServer
Serving HTTP on port 8000 ... - - [16/May/2016 16:34:37] "GET /pview-panda.html HTTP/1.1" 200 - - - [16/May/2016 16:34:37] "GET /pview-panda.html.mem HTTP/1.1" 200 - - - [16/May/2016 16:34:37] "GET /pview-panda.js HTTP/1.1" 200 - - - [16/May/2016 16:34:37] "GET / HTTP/1.1" 200 - - - [16/May/2016 16:34:44] "GET /pview-panda.html HTTP/1.1" 200 - - - [16/May/2016 16:34:44] "GET /pview-panda.html.mem HTTP/1.1" 200 - - - [16/May/2016 16:34:44] "GET /pview-panda.js HTTP/1.1" 200 - - - [16/May/2016 16:34:44] "GET / HTTP/1.1" 200 -

^CTraceback (most recent call last):
  File "/usr/lib/python2.7/", line 174, in _run_module_as_main
    "__main__", fname, loader, pkg_name)
  File "/usr/lib/python2.7/", line 72, in _run_code
    exec code in run_globals
  File "/usr/lib/python2.7/", line 235, in <module>
  File "/usr/lib/python2.7/", line 231, in test
    BaseHTTPServer.test(HandlerClass, ServerClass)
  File "/usr/lib/python2.7/", line 599, in test
  File "/usr/lib/python2.7/", line 231, in serve_forever
  File "/usr/lib/python2.7/", line 150, in _eintr_retry
    return func(*args)

it is first time i use ubuntu, everything is new to me


You may need to call init_libpandaegg(); at the beginning of your main function (include pandaegg.h) to force the .egg loader to be linked in. The same may apply to inint_libwebgldisplay() (not sure).

You should open the JavaScript console in your browser and look at what it says there. It should give you the error message there.