How to Vite with Babylon.js (Game Tutorial Series)

NOTE: A more general-purpose (and beginner-friendly) version of this guide is now available on the Babylon Docs!

Hi! Are you allergic to Webpack like I am? Do you hate dealing with its clunky config files? Thankfully the 2020s have given us Vite, which is simpler to use. But if you’re following the Create a Game Tutorial Series like I am, you’re probably wondering how to use it with Babylon.

This guide is intended to replace the Getting Set Up chapter in the tutorial series, but you should read Getting Started first if you haven’t used Babylon.js before.

  1. Create a folder for your code project(s) and open it with a code editor like Visual Studio Code (or VSCodium.)

  2. Install npm (either through here or your package manager if you’re running Linux).

  3. Close and reopen your code editor to the same folder and then run each of these commands in the Terminal at the bottom after you do that:
    npm i vite (i is short for install)
    npm i -D @babylonjs/core (-D is short for --save-dev)
    npm i -D @babylonjs/inspector
    npm i -D @babylonjs/gui

  4. Now run npm init vite and give your project any name you want, select “Vanilla,” and then select “TypeScript.” A project folder with the name you chose should now appear on the left-hand side. Feel free to rename this folder if you’d like.

(NOTE: The reason why we didn’t install the babylonjs packages inside the actual project folder that Vite just created is because it saves on space. Why install another copy of babylonjs in every project folder, when you can install it in the root folder above all your future game projects? You can use npm packages installed in folders ABOVE your working directory, not just in the same folder as your code.)

  1. Right-click this folder and click “Open in Integrated Terminal.” You should now have a second terminal window open that lists the name of your new generated project folder. Run npm i and then npm run dev. If you ctrl+click the localhost: link in the terminal, you should be taken to a functioning test page within your browser:

  1. Go back to your code editor and press Ctrl+C in the Terminal to stop Vite from running. Using the left-hand side bar, do the following:
  • Delete everything inside the src and public folders
  • Inside the src folder, create a file called app.ts
  • Double-click index.html and tsconfig.json to open them

image
*ignore the dist folder

  1. Open index.html, paste in the following, and save:
<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>Title of Your Project</title>
    </head>
    <body>
       <script type="module" src="./src/app.ts"></script>
    </body>
</html>

(This is the same code as the lesson but with the addition of a script tag to indicate our primary TS file.)

  1. Open app.ts, paste in the following, and save:
import "@babylonjs/core/Debug/debugLayer";
import "@babylonjs/inspector";
import "@babylonjs/loaders/glTF";
import { Engine, Scene, ArcRotateCamera, Vector3, HemisphericLight, Mesh, MeshBuilder } from "@babylonjs/core";

class App {
    constructor() {
        // create the canvas html element and attach it to the webpage
        var canvas = document.createElement("canvas");
        canvas.style.width = "100%";
        canvas.style.height = "100%";
        canvas.id = "gameCanvas";
        document.body.appendChild(canvas);

        // initialize babylon scene and engine
        var engine = new Engine(canvas, true);
        var scene = new Scene(engine);

        var camera: ArcRotateCamera = new ArcRotateCamera("Camera", Math.PI / 2, Math.PI / 2, 2, Vector3.Zero(), scene);
        camera.attachControl(canvas, true);
        var light1: HemisphericLight = new HemisphericLight("light1", new Vector3(1, 1, 0), scene);
        var sphere: Mesh = MeshBuilder.CreateSphere("sphere", { diameter: 1 }, scene);

        // hide/show the Inspector
        window.addEventListener("keydown", (ev) => {
            // Shift+Ctrl+Alt+I
            if (ev.shiftKey && ev.ctrlKey && ev.altKey && ev.keyCode === 73) {
                if (scene.debugLayer.isVisible()) {
                    scene.debugLayer.hide();
                } else {
                    scene.debugLayer.show();
                }
            }
        });

        // run the main render loop
        engine.runRenderLoop(() => {
            scene.render();
        });
    }
}
new App();

(This code is unaltered from the original lesson. Take note of how the import statements allow the rest of the code to work, as well as how this structure should be divided into functions and class variables as you progress.)

  1. Open tsconfig.json, paste in the following, and save:
{
  "compilerOptions": {
    "target": "es6",
    "lib": [
      "dom",
      "es6"
    ],
    "useDefineForClassFields": true,
    "module": "ESNext",
    "rootDir": "src",
    "moduleResolution": "node",
    "resolveJsonModule": true,
    "noResolve": false,
    "sourceMap": true,
    "noEmit": true,
    "preserveConstEnums": true,
    "isolatedModules": true,
    "esModuleInterop": true,
    "noImplicitAny": false,
    "noUnusedLocals": false,
    "noUnusedParameters": true,
    "noImplicitReturns": true,
    "skipLibCheck": true
  },
  "include": [
    "src"
  ]
}

(This tsconfig.json file is a mix between the one provided in the tutorial and the one generated by Vite. I wrote a more efficient version of this file for the Babylon Docs version of this tutorial, but this works fine too. You can read more about how this file works in the TypeScript docs.)

  1. Run npm run dev in that second terminal and ctrl+click the localhost: link again to display it in your browser… and be amazed!

And just like before, you can press Ctrl+C in the terminal window to stop Vite from running.

Isn’t that so much cleaner to handle? Now you can turn your project into a Git repository if you want, and then move on to the next chapter. UPDATE: As you complete the rest of this tutorial, please keep the following in mind…

  • The lessons are a little unfinished- this series is more of an explanation of this game’s code rather than a step-by-step tutorial to make it. There’s also missing links to assets that you’ll need to grab yourself from the GitHub repo, such as textures or sound files. I was not able to run any code until I got to the Game GUI chapter that introduces ui.ts, and it was still crashing while looking for un-introduced sound files…

  • Static assets and their respective folders (such as images/models/sounds/etc.) still go in the public folder so that they are included in any compiled bundles you make. If you get assets that don’t load in for some reason, try adding ?url to the end of your code’s filepaths. Some uncommon file types (such as .glsl shader code) may need ?raw at the end to tell Vite to import the file as a string. This means /shaders/example.glsl would become /shaders/example.glsl?raw.

  • OPTIONAL NOTE: This technically goes against the Vite docs’ recommendation of making every static asset’s path into an import statement, but you’ll have to create a .d.ts file in your src folder and figure out this code snippet if you want to do it this way. There really isn’t a need to do this unless you want to obscure the filenames of your assets (which doesn’t prevent players from ripping assets from your game) and also have the browser cache these large files locally. Caching your assets may irritate some players of your game since it’ll take up extra space on their computers, but doing this could theoretically allow them to play an offline copy of your game. I haven’t tested this though.

  • ALSO OPTIONAL: If you really want to optimize your code and shrink your compiled bundle sizes, you can turn your import statements into dynamic ones. I haven’t tested if this works with Babylon.js yet either, but this video can help you get started if you want to explore that.

BUILDING AND SHARING ONLINE:

  • Once you reach the end of the tutorial series and address the extra notes above, you can build the final deployment of the game with npm run build and then test it with npm run preview. If everything works as intended, you can find the exported production code in the dist folder. BEFORE you share it online, be sure to open the index.html file and edit the "/assets/index.(...).js" string to add a period before the first slash. (Explanation for why this happens is in the next bullet point.) Now you can host online wherever you’d like!

  • Once you build and host this code online, you may have a weird glitch where lit lanterns have weird red glowing patterns instead of their usual yellow appearance. This is caused by a typo left by the original developer. To fix this, go to environment.ts, locate the "/textures/litLantern.png" string, and add a period in front of the first slash. (As you may have noticed, browsers can’t find folders in your root structure unless you have no preceding slash "like/this", or one with a period "./like/this", because "/doing/this" confuses the browser.)

  • If you’re unable to compile your code at all because of a leftover error, it’s probably because of res in app.ts, which can be resolved by adding a leading underscore to it like this: _res. I don’t know why TypeScript wants us to annotate the variable like this, but it works.

And that’s how you use Vite with Babylon. Enjoy!

4 Likes

This is amazing, wonder how we could surface those info in the doc @PirateJC ???

1 Like

Whoa @wavetro this is awesome!

Any interest in writing this up as a more official doc for the Babylon.js Docs pages? Perhaps under “Guided Learning?”

1 Like

I’d be willing to! Is there a GitHub repo for the docs that I should fork to rewrite this?

1 Like

Yup! Here ya go!

1 Like

Thanks! I’ll write and submit a pull request for this once I complete the rest of the Create a Game Tutorial Series so I know for sure that Vite is compatible

Hi @PirateJC, I finished the tutorial series, but in order to compile the code for production, it looks like I have to store every asset’s filepath in a variable via an import statement. These import statements are how Vite locates and bundles the assets needed for the final compiled export.

This would mean someFunctionHere('./path/to/file.ext') would have to become

import exampleFile as './path/to/file.ext';

// and then later...

someFunctionHere(exampleFile);

Is it alright if I still do a write-up of this Vite workflow, but without referencing/connecting back to the Create a Game Tutorial Series? It’s easier to tell users to get into the habit of doing import varName as './path/to/file.ext' with whichever project they’re tackling next, instead of making them edit the existing Summer Festival game code to turn every asset filepath into an import statement.

2 Likes

Ah yes! I see. Yeah no problem at all. I still think it’d be great to have the content, even if it’s more general in nature and doesn’t point specifically to the Create A Game Tutorial Series!

Thanks for doing this @wavetro ! We LOVE to see people contribute to the greater community!

Cheers!

1 Like

Can you put the assets in the /public folder that you deleted and they’ll be served? Importing assets is going to increase bundle size and affect caching.

1 Like

I could try that, but isn’t it safer to bundle the assets so the filenames are hashed? Or does the extra security not matter

EDIT: Nevermind you’re right, any end user would be able to find and download the assets anyway. I’ll update the tutorial soon

1 Like

Thank you everyone! A more thorough version of this tutorial is now up on the Babylon Docs.

2 Likes