Script load order


I’m attempting to get a workflow with typescript going, using Poseidon to upload locally generated js-files. I’m getting some reference exceptions when referring to classes in other files, which seems to be remedied by script load order. However, only a few of the scripts will allow themselves to reordered, while the rest just snap back into place when I release them. To make matters more interestning, every time I refresh the editor, the order has changed (possibly because it triggers a reupload by Poseidon).

Any ideas or suggestions?

Hi @Mr_T,

Poseidon is in beta, so there might be issues. I didn’t have though any issues reordering PlayCanvas scripts that are created in JS files uploaded with remote coding.

What is your workflow? You upload a single JS file generated from your typescript codebase?

We are doing something similar in the Ermis Special Effects, single JS file containing 4 different pc.Scripts. Changing the order of the scripts works as expected.

Is it possible to share a sample project or add me (leonidas) to your project to take a look?

1 Like

Hey @Leonidas, thanks for the quick reply!

I’m honestly fairly inexperienced with both js/ts and playcanvas (C# dev mostly), so still working out how to do this and to get the most out of typescript in the process. Currently I just create scripts as you would normally, just in ts - then tsc watch will compile new ts-files into js, which finally is uploaded through pic-serve. Whether or not this is a valid approach I have yet to find out :slight_smile: As such, I get a lot of js-files (or I will at some point) instead of one big file.

So what I was trying to do was to define a class in one file and consume it in another. I tried using export/import, but playcanvas wouldn’t have it. Still, defining it in one file and consuming it in another should be fine, I reckon? The editor definitely doesn’t like it, as it seems to parse each script individually looking for attributes and whatnot. But playing the game would work without errors, when the load order was randomly ok (ie the class is defined before the consumer). Again, attempting to reorder the scripts would have (most of) the scripts just snap back to where they were. And reloading the page would change the order up in a seemingly random fashion.

Before I was guided to Poseidon, I was trying to use the typescript template from snowfrogdev. It does indeed compile everything to one file, so I’m sure there’s a good reason for it. It even had autoupload, albeit a bit more cumbersome to setup. It sadly hasn’t been updated in 2 years. It had a quite useful decorator “createScript”, which basically turned your classes into scripts. I have been trying to make it work but I’m not far enough along the learning curve yet it seems :slight_smile:

I’d very much appreciate a suggestion for a proper ts-workflow with PlayCanvas! I’ve been looking everywhere, but there’s not a whole lot to find.

I’ll make you a sample project tomorrow!

Indeed Typescript is a life-saver for people coming to Javascript from C# or any other strongly typed language.

Here is how I configure my projects, when doing TS development, to have a clean (and debug enabled) build.

My tsconfig.json during development:

  "compilerOptions": {
    "target": "ES2015" /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019' or 'ESNEXT'. */,
    "module": "none" /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', or 'ESNext'. */,
    "outDir": "./dist" /* Redirect output structure to the directory. */,
    "rootDir": "./src/" /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */,
    "strict": true /* Enable all strict type-checking options. */,
    "typeRoots": [
    ] /* List of folders to include type definitions from. */,
    "esModuleInterop": true /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */,
    "inlineSourceMap": true /* Emit a single file with source maps instead of having a separate file. */

This will indeed produce several files, one for each .ts source file. For small projects this might be OK, for larger it starts to get slow and messy having to upload several files to your Playcanvas project.

Webpack to the rescue, this will take care of building the .ts project on each file change and upload a single file (that can contain multiple pc.Script types) to Playcanvas.

Here is my webpack.config.js, there are plenty of tutorials online on how to configure webpack to work with typescript (shoot if you have any question!):

const path = require("path");

module.exports = {
  entry: "./src/index.ts",
  mode: "development",
  module: {
    rules: [
        test: /\.ts?$/,
        use: "ts-loader",
        exclude: /node_modules/
  resolve: {
    extensions: [".ts", ".js"]
  output: {
    filename: "ermis-special-effects.js",
    path: path.resolve(__dirname, "dist")

Webpack requires an entry point to your Typescript application, which in a Playcanvas context means your code should be running at least one instance of a pc.createScript() method automatically (the editor parses your JS code searching for these declarations).

On how to define your Playcanvas scripts in Typescript: I don’t do anything fancy yet, I opt to use any and standard JS methods:

const PositionEntity: any = pc.createScript("positionEntity");

// initialize code called once per entity
PositionEntity.prototype.initialize = async function() {
  const entity: pc.Entity = this.entity;

  for (let index = 0; index < 10; index++) {
    entity.translate(1, 0, 0);

    await sleep(150);

// update code called every frame
PositionEntity.prototype.update = function(dt: number) {};

// --- Utilities
function sleep(ms: number) {
  return new Promise(resolve => setTimeout(resolve, ms));
1 Like

Thanks a bunch!

I am now running webpack and compiling to a single file. It does take care of the load order issue (though still curious about what went wrong there). I even got the decorator working, so I am a happy camper!

I see you have ES6 as target, is it safer/better to use ES5 or is ES6 fine? I see the playcanvas script editor is flagging a lot of warnings at least…

Have a great weekend :slight_smile:

1 Like

During development sometimes I prefer doing ES6 since the compile times in larger codebases can be smaller.

But for production I most likely will go for ES5, unless my deployment target doesn’t really care about older devices/browsers.

Yeah, the editor doesn’t fully support ES6, even with a special flag (I miss await/async for sure). But since TS produces inline source maps, I don’t really care since I debug on my IDE, can pinpoint most of the time the line number in error.

Bravo that you nailed it! Typescript is a great thing, combined with Playcanvas and local development, can improve the development experience tenfold.

1 Like