begin
Writeln('Hello from pas2js!');
end.
Warning
|
This feature is in intensive development now. As such, this page will change as we go forward. Stay tuned! |
Web target allows you to recompile any application you wrote using Castle Game Engine (using TCastleWindow
) to the web. So you can put your application on a website, as part of a regular HTML webpage, and users can play it instantly, without the need to download / install any native application or browser plugin.
3D viewport, with animated light, dropping boxes with physics (Source code in examples/web/simplest_viewport)
2D "Invaders" game (Source code in examples/web/simplest_invaders)
The first, simplest example that runs on the web! (Source code in examples/web/simplest)
Warning
|
These are early demos, so don’t judge the final quality by these demos (yet) :) We have some known TODOs, including:
|
Note
|
Things below look complicated? We have plans to make them easier:
|
Installing the tools:
Warning
|
We test with the latest Pas2js from GIT right now, so this is also what we advise to use. The stable release 3.0.1 from https://getpas2js.freepascal.org/ is not supported right now (due to incompatibilities around Job.Js and friends usage, which are all solved in latest Pas2js + FPC). With time we may change this (adjust to older Pas2js) and/or a new release of Pas2js may happen which will make things easier :)
|
Test that it works. Create the following simple program, save it as hello_pas2js.lpr
:
begin
Writeln('Hello from pas2js!');
end.
If everything is installed and configured OK, then you should be able to compile it using this from the command-line:
pas2js -Jc -Jirtl.js -Tbrowser hello_pas2js.lpr
If everything went well, you should get a file hello_pas2js.js
in the same directory.
Note
|
If It does seem that |
compileserver from Pas2js.
This is a very useful tool distributed along with Pas2js. Make sure you got it, test it by executing on the command-line this:
compileserver
Leave it running and access http://localhost:3000/
in a web browser.
Kill it by Ctrl+C in the terminal.
Why is this useful? Most dynamic webpages should be tested through a webserver running on your system (like compileserver
, though you can also spin a full Apache or Nginx) that serves the HTML, JS, WASM and other files. You view them using a http://localhost:…/
URL, which means you access the webserver on your own system. In contrast, just opening the HTML files in a web browser (which results in URLs like file:///…/index.html
) will usually not work, because of security: JavaScript code on such pages will not be able to load extra resources (like WASM files). It’s possible to configure your browser to circumvent this (disable CORS checking) but it’s a hassle. Using a "temporary webserver" like compileserver
is the easiest way to test your applications.
FPC with WebAssembly support.
In our opinion, it’s easiest to get this using fpcupdeluxe: install FPC "trunk" version, then install FPC cross-compiler for OS=Wasi
and CPU=Wasm32
. You can also just get it from GIT and compile manually, following FPC wiki about WebAssembly.
Test that it works. Create the following simple program, saved as hello_wasm.lpr
:
begin
Writeln('Hello from WebAssembly!');
end.
If everything is installed and configured OK, then you should be able to compile it using this command:
fpc -Twasi -Pwasm32 hello_wasm.lpr
If everything went well, you should get hello_wasm.wasm
in the same directory.
Download the latest Castle Game Engine as usual.
Since we have merged the web target into the master branch, you don’t need to do any special things to get it. But of course, if you want to "build from scratch", you can checkout the master branch using GIT and then follow compiling from sources docs.
Enter any CGE project that was tested for web and compile and run it for web.
You can do it using the editor. Switch the platform using "Run → Platform → Web", then click "Compile And Run" (F9).
Or you can do it from the command-line, using our build tool:
castle-engine compile --target=web --mode=debug
castle-engine run --target=web
Warning
|
Beware of the cache! Browsers may cache the contents of everything. We counter this to some extent, by adding a random (determined at compile-time) suffix to URLs of everything we access:
But if you still run into cache issues (it seems like you see the previous application version, or even a different application that was run previously from the same server) → just refresh using Ctrl + Shift + R. TODO: We should make it configurable, to enable turning off "fighting with cache" for reproducible-builds that should not have random suffixes in the generated files. |
Note
|
Our default HTML template displays log underneath your application, including everything you write to the log e.g. using WritelnLog('Hello!') . The log is also accessible using the "Developer Tools" of your browser (press F12) → "Console" . The console can also be toggled when in the fullscreen mode.
|
You can customize the default canvas size using the web options in CastleEngineManifest.xml.
In the editor: Select the new platform, like "Web", in the "Run → Platforms → …" menu. Then you can save it for the future using menu item "Run → Platforms → Save Chosen Platform As Default (in Project Local Settings)".
Alternatively and equivalently:
You can create a file CastleProjectLocalSettings.json
in your project. Place it at the top-level of the project, so it is next to the CastleEngineManifest.xml file. The CastleProjectLocalSettings.json
can contain settings that are specific to your local user or machine.
For example, it can specify that the default target is web like this:
{
"$$ClassName" : "TCastleProjectLocalSettings",
"UsePlatformDefaults" : true,
"DefaultTarget": "targetWeb"
}
Changing the default platform is useful if you want to always build and run for the web, without specifying --target=web
every time. Now using just castle-engine compile
and castle-engine run
will build and run for the web. You can still override this at command-line, using the --target
, --os
, --cpu
options (see build tool docs).
Note
|
The CastleProjectLocalSettings.json support is not strictly a feature of the web target, it’s useful in every case when you want to change the default platform (and we plan to add there more things). By default, we build and run for the current desktop target, with current OS and CPU.
|
All the possibilities are described by the TCastleProjectLocalSettings
.
Note that you can also open the CastleProjectLocalSettings.json
as a design in the editor and edit the component in the object inspector. That’s because TCastleProjectLocalSettings
is a regular serializable component for our engine. Right now, this doesn’t really imply any new features, but it may in the future, and it will be compatible with future CastleProject.json.
Warning
|
The file CastleProjectLocalSettings.json should not be committed to the version control, it should be ignored by file like .gitignore (if you use GIT).
|
I want to thank everyone involved in this and let’s push forward! Web target is a really cool feature, from my talks I know it’s an important feature for many CGE users, and I feel we have it in our reach. Let’s keep coding and enjoy making games :)
Absolute :)
The resulting application works in any modern web browser and does not require user to install anything special (like old plugins). Everything we need is already built-in in all modern web browsers, in particular WebAssembly and WebGL support are completely standard now.
All the web browsers should be good. Firefox, Google Chrome and derivatives (like Vivaldi) are all good.
Mobile web browsers are supported as well. We support touches in a way consistent with mouse events, just like on non-web mobile targets. We tested with both Firefox and Google Chrome on Android.
Warning
|
To make the resulting web application working also in mobile (like Android) web browsers, one needs to apply a 1-line hack to pas2js sources, see below for details.
|
WebAssembly is not limited to running in a web browser. We support running the compiled WASM binaries also using Wasmtime, though without rendering.
Warning
|
Trying to render anything in Wasmtime will fail, as we don’t have access to WebGL API and context (or the rest of JS API, like
and we don’t have immediate plans to make it work. But, at least everything else (all the code up to the rendering) will work on Wasmtime. |
Build your application like this (on the command-line):
castle-engine compile --os=wasi --cpu=wasm32 --mode=debug
Run it like this:
export WASMTIME_BACKTRACE_DETAILS=1 # get a more detailed backtrace
wasmtime <project-name>.wasm
The main use-case of this is additional debugging approach. In case of a crash, the wasmtime
will give you a useful stack trace with line numbers (if only you compiled with --mode=debug
, as shown above).
As a particular application of the above ("you can run engine code using wasmtime, except rendering"), you can run our test suite on WASM.
Warning
|
Making castle-tester.wasm work this way is a work-in-progress. The time_measurements_tests.wasm mentioned below works already.
|
cd tests/
castle-engine compile --os=wasi --cpu=wasm32 --mode=debug
wasmtime castle-tester.wasm --console --no-window-create
cd time_measurements_tests/ # enter tests/time_measurements_tests/ in CGE
castle-engine compile --os=wasi --cpu=wasm32 --mode=debug
wasmtime time_measurements_tests.wasm
We’re using FPC WebAssembly target.
Huge thank you go to the whole FPC team for making it possible! For general documentation (unrelated to CGE), see this article by Michael Van Canneyt.
To run the WebAssembly in a web browser, there is additional "glue" code, which we also write in Pascal and compile using Pas2js.
The integration between Pas2js and FPC + WebAssembly is extremely easy, again thanks to the work of everyone involved!
For development purposes, we can run a simple webserver on the localhost. Pas2js features a ready compileserver
, we just use it.
Note
|
FpWeb also can instantiate a standalone HTTP server in just a few lines of code. But it seems we don’t need even this, compileserver is all that we need.
|
Our build tool takes care of everything. You usually want to execute 2 commands:
castle-engine compile --target=web --mode=debug
This command:
generates web-specific files (in castle-engine-output/web/
),
compiles the glue code using Pas2js,
compiles the application code using FPC for WebAssembly.
castle-engine run --target=web
This command:
runs web server (compileserver
) on http://localhost:3000/
to serve castle-engine-output/web/dist/
,
runs web browser (default web browser on your system, logic using our OpenUrl
) to open http://localhost:3000/
.
Note
|
For some specific applications (like our
This is not equivalent to Compiling the standalone program with |
Our editor can also build and run for web. Underneath, it just calls the build tool commands described above (which you can see in the "Console").
Just change the platform using "Run → Platform → Web" menu item and hit "Build and Run" button or menu item (F9).
You can also set web platform as default.
We create a HTML file with the <canvas id="castle-canvas">
element (see canvas docs at MDN). This HTML runs the JS code (compiled by Pas2js) that will in turn run the WASM code of your application, which will initialize and use WebGL context on this canvas.
This HTML file can be customized / replaced / rejected as you wish. It only needs to contain a reference to the generated JavaScript file using <script>
and a <canvas>
element with ID castle-canvas
. You can surround it with any content and style.
For nice look, our generated HTML also uses Bootstrap, but again: this can be customized / removed. We don’t depend on any special CSS or JS libraries.
Some alternative HTML templates may be available in the future, as well as the ability to provide your own template. For now, just (before building) customize the engine’s tools/build-tool/data/web/dist/index.html
file or (after building) customize the generated castle-engine-output/web/dist/index.html
(e.g. replace it with whatever contents you prefer, or edit the lines you want, manually or automatically, e.g. using sed
).
We use WebGL API from WebAssembly. All our rendering code has been adjusted to WebGL!
First step to do this was the JOB units implemented in Pas2js + FPC. This is a collection of units (on both Pas2js and FPC sides) that cooperate with each other. The goal is to allow our WASM applications to access WebGL API (defined in this WEBIDL file) plus a few other JavaScript APIs we need (for HTML canvas, window.requestAnimationFrame
etc.).
Note
|
The WebGL API is available in the browser to Pas2js (that is, any JavaScript code in the browser). If not for JOB, we would expose this API for the WebAssembly, following the canvas example described in the 2nd part of this article. But, well, using JOB makes things even easier. |
We generate a unit CastleInternalJobWeb using this script and WEBIDLs: https://github.com/castle-engine/castle-engine/tree/master/src/base_rendering/web/webidl .
Note
|
FPC ships an example job_web.pas that already contains everything we need, but it’s a huge unit. It has ~184 thousands of lines and compiling it is slow (this is esp. problematic as current FPC 3.3.1 re-compiles units more often than it should — it seems that every change even to implementation causes rebuild of everything derived, as if we changed interface). That’s why we generate our own unit, CastleInternalJobWeb (merely ~19 thousands of lines) that contains only what we need. |
Second step: We then generate include file castleinternalwebgl_flat_api.inc that "flattens" the API exposed in CastleInternalJobWeb, making it more similar to the OpenGL ES API in the CastleGLES unit. This generation also consults WEBIDL, to process all WebGL methods we need. The tool that does this is tools/internal/generate_webgl_flat_api.
Third step: We then use CastleInternalWebGL unit as (to a large extent) a drop-in replacement for the CastleGLES unit.
Lastly, we manually adjusted rendering code to account for final, unavoidable differences between OpenGL ES (CastleGLES) and WebGL (CastleInternalWebGL). Mostly straightforward work, differences stem mostly from the fact that OpenGL ES is a C API (so: no such thing as Variant
; and occasional pointer usage to pass arrays or "raw" buffers), while WebGL is a JavaScript API (so: everything is an array, even "raw" buffers; some reasonable usage of Variant
types, like getParameter
results). Just a small number of {$ifdef CASTLE_WEBGL}
clauses in strategic places got the job done.
In the end, we render using WebGL 1.0, with optional WebGL 2.0 features. This is very similar to our current rendering approach on mobile, where we use OpenGL ES 2.0, with optional OpenGL ES 3.0 features.
Input: We handle pointer events on the canvas (down, motion, up) thus we handle mouse and touch input. We also handle key events (key down, key up) on the whole DOM window.
Warning
|
TODO: key handling may change to rely on focus instead, to allow to e.g. have 2 web applications using CGE on the same page. |
We can switch to fullscreen. The default index.html
features a button to do this.
Rendering is crisp, in "real device pixels", including on high-DPI screens and when you zoom the page.
For application data, we pack the data as one binary file (zip). This allows to download the complete data at a clearly specified moment, with a nice progress bar, and later just load data files synchronously and reliably.
Zip is in this case both a container for multiple files, and provider for basic compression. Frankly, we use it more "container for multiple files" here than compression. Because http(s) communication between any modern web browser and server will add a gzip compression on top of this anyway.
We used zip, as the simplest and most common archive format, supported by a myriad of software on all systems, with tools to unpack available even out-of-the-box in all modern systems. It is also consistent e.g. with id Software pk3 (which was really a zip file).
We use our TCastleZip
to handle ZIP. It uses FPC and Delphi standard units.
Fonts: If your project uses TTF / OTF / WOFF files (like Platformer game (Source code)):
Since FreeType is not available on the web, we generate an embedded font, just like when using texture-font-to-pascal
, for all fonts found in your data at castle-engine compile
. Then we compile the fonts in, by linking the WASM library with CastleAutoGeneratedAllFonts
unit.
This approach is not final, as it has a number of problems: We assume hardcoded size (20), hardcode character set (ASCII). Also it cannot work on Windows (because build tool right now doesn’t use dynamic libraries, to avoid conflicting with removal/updating of DLLs of the project). See TODO list below for plans how to improve.
PBR shaders (used by most 3D demos) combined with textures have weird error at compilation on WebGL.
Reproduce with clean sample and submit to FPC:
Running on mobile requires now hacking pas2js, in pas2js/packages/job/src/job_browser.pp
, find and comment out RegisterGlobalObject(caches,'caches');
. Otherwise both Firefox and Chrome on Android fail with ReferenceError: caches is not defined
.
For audio, we will add a new sound backend using WebAudio. This will be the default sound backend on the web (like the OpenAL is now the default backend on non-web platforms).
It is also possible our FMOD sound backend will also be ported to web, as FMOD supports HTML5. This would make FMOD a truly cross-platform sound backend, working on every platform we support in CGE.
Note
|
About WebAudio in the context of X3D 4.0. Supporting advanced X3D 4.0 audio nodes is not our priority, but since WebAudio will happen anyway, on the web… they will open this possibility. There is also LabSound that provides WebAudio-like API on non-web platforms, and in principle it could one day replace OpenAL, making web and non-web audio handling closer. |
Possibly throw in additional compression of data and/or wasm executable?
WASM size: while it started smaller (compilation of examples/platformer, which practically uses 100% of CGE units, yields a binary platformer.wasm
that has 16 MB. Gzipped it has 3.4 MB.) … but it grew larger, to 40 MB. TODO: investigate why. JOB? IFC?
The gzipped size is really what matters — both web browsers and servers support gzip-(de)compression on the fly, you can also just put ready-gzipped version on the server and tell the browser to just decompress. So in all practical cases, users will download 3.4 MB, not 16 MB.
We could also use Brotli, a newer compression method also commonly supported by web browsers and servers.
Implement TCastleDownload
for web.
Is it good we capture keys by listening to keydown
and keyup
events on the whole window
? Could conflict with other things on the same page trying to handle keys (like 2nd CGE web application). Should we rather rely on canvas being focused, or maybe some canvas div wrapper? (but will need code to make it focused, because out-of-the-box canvas doesn’t get any keydown / keyup).
Save the user settings / save games (CastleConfig) to webpage persistent storage.
Open / save files by browser dialog (TCastleWindow.FileDialog
).
Block context menu (maybe make it optional) to allow using right mouse button comfortably?
Handle scrollbar.
Fix reading fonts by the build tool on Windows. Right now this is disabled because build tool right now doesn’t use dynamic libraries, to avoid locking (thus preventing removal/updating) of DLLs of the project. We need to enable loading DLLs in build tool such that it will not lock project DLLs.
Embedding fonts must be improved.
Current problems:
High-level problem: We want to move away from "embedding things by compiling them into Pascal" like by image-to-pascal
, texture-font-to-pascal
.
Because this is causing a maintenance burden (programs and units to convert things to specialized Pascal code). It does not "scale" (e.g. to distance field fonts) because each idea would need a new converter X → Pascal. texture-font-to-pascal
is deprecated for above reasons.
And because this is unnecessary. For embedding things in EXE, FPC already has "cross-platform resources" with nice API and it just allows to embed a binary blob. And we don’t even need embedding things: we have "data organized in subdirs and files" on all platforms, including the web.
In other words, thinking higher-level: We don’t need to solve "how to embed" problem. We only need to solve "how to read the data format (efficiently, without FreeType)" problem. And in general "embedding" is a different task than "making format easier to read", we don’t need "embedding things by compiling them into Pascal" to be a combined solution to both things.
What we really want is to convert the font file (TTF / OTF / WOFF) to our own font format. And keep it in data/
. Reading this format should:
not require anything external, like FreeType.
be efficient.
avoid as much run-time processing as possible. Do pre-processing earlier. E.g. we have to eliminate current processing of distance field fonts at run-time.
be useful on all platforms. It would be easier if we didn’t need to care about FreeType distribution for e.g. mobile or console targets.
So we need own font format.
One part of it should JSON serialized using our CastleComponentSerialize
.
Maybe just serialize TCastleFont
? It only has stuff for generation, not display (OptimalSize
, not Size
), which is good for this use-case. Through it lacks glyphs and font metadata like family name — IOW things that are "output from FreeType". With xxx.castle-font
(simple consistent extension is a big plus here.)
Or existing TTextureFontData
? Remake it to be TCastleFontData
, a TComponent
descendant, with consistent property names (Size
→ OptimalSize
). Save it as xxx.castle-font-data
.
Make a dedicated editor for above component that, when saving, also saves font texture (distance field or not) alongside it and glyphs (as children components). A bit like sprite sheet editor saves atlas.
TCastleFont.Load
should allow reading xxx.castle-font[-data]
, and then read texture alongside too.
To improve this documentation just edit this page and create a pull request to cge-www repository.