Documentation developer's technical documentation, writen by Quintus Hegie for computer geeks and train enthousiasts. With this documentation you'll be able to develop new levels and create new game objects (locomotives and/or wagons). If you're a web developer and you have a question about 3D progamming and/or BabylonJs, please contact me on (until December 8th 2018: HTML5GameDevs). If you're a game player instead of a developer, then you can find the manual here.

Please note: The game specific soure code is protected with legal copyright! You may not use this code to create your own train or similar game, but you can use some components and logic for any other type of game. The code may be changed at any time without any notification, rendering any custom additions maybe no longer compatible with the latest version.

3D Engine

Scene Optimization

In comparison with regular train simulator games with large worlds that are partly visible, my game is about model trains where the whole play world fits into a single room so to say. The player thus generally has a good camera view over the whole level (room). Therefore most meshes are visible and need to be rendered most of the time. This creates a challenge for keeping a constant and high framerate, because only a little fraction of the meshes will be out of view most of the time. That is why I use some specific scene optimization techniques for this train game.

Texture Quality

To main save on memory use (for low end devices) and network bandwidth (slow connections) and server traffic (costs), the textures used in the game come in several qualities. We set the quality mainly by choosing the resolution or pixel dimensions (width and height) of the texture. Depending on the type of texture contents, we choose for a JPEG (photo/realistic) or PNG (drawing/cartoonistic) compression technique and file format. The GPU generaly won't care which format we use, and often has enough RAM, but if the player has a low end device with no GPU then we want to reduce the load on CPU and normal memory.

Texture Qualities
Quality Setting Maximum pixel side dimension Average compressed file size
Low64-128 pixels10-25KB
Medium256-512 pixels50-250KB
High1024-2048 pixels500KB-2MB

Some gameobjects use non-square textures (e.g. a rectangular texture for sphere materials). For these textures the maximum pixel dimension to take is that of the longest side (width or height). So a low quality rectangular texture for use on a sphere mesh may be 128×64 pixels.

For sprite atlas textures, the dimension of a single sprite may not exceed the specified limits. So a low quality sprite atlas which holds 4 square sprites, may be at most (4×128)×(1×128), or (1×128)×(4×128) or (2×128)×(2×128) pixels in size.

Level of Detail (LOD)

A level mainly consists of a terrain or playing ground, that is visible in full or in large part. Next in line of frequently visible meshes are the train tracks. The train tracks may be observed from very close by (when camera is set to train drivers view mode) as well as from a large distance (when camera is set to level map view). A technique to speed up rendering when using various distances to view the same mesh is to apply Level of Detail (LOD).

Level of Detail (LOD) on a Rail
Levels of Detail on a Rail: left the full mesh (rendered when viewed from close-by), right the simplified mesh (rendered when viewed from a distance)

Game Engine

General logic

Config file

The game consists of many different game object types, which each have their own unique capabilities. To keep the game logic as simple as possible, I chose to use a config or settings file to set up a specific game object type.

This config file contains the properties to set for the different game objects. This way it would be easier for me to tweak the game settings in order to get the best gaming experience. The specific game object's type config will be loaded in the GameObject definition from the settings.js file.

Game Objects

A game object (GameStateObject in code) represents an object that is of interest of the game play.

unique_idThe unique ID for this object (compulsory, if omitted, a random number will be chosen)12
nameA unique internal name for this object (optional)locomotive1
groupTo which group this object belongs (optional)stations
labelA visible label for this object (optional)Steam locomotive
teamThe team number this object belongs to (optional)2
parentSet to the name of the parent object, if you want a child relationship (optional)train1



A locomotive is equiped with a motorblock and is controllable by a player.

Locomotive Game Object properties
Locomotive Game Object: accessors of attached objects


A wagon is not motorized and needs to be pushed/pulled by a locomotive.

Wagon Game Object properties
Wagon Game Object: accessors of attached objects


A train is a logical unit that consists of one or more vehicles (array). Because this is a train game, the vehicles in a train are typically rail vehicle game objects like wagons and locomotives. A train has at most 1 master, that is the unit that controls or moves the train (often the 1st locomotive).

Train Game Object properties
Train Game Object: accessors of attached objects and constraints

A train vehicle sequence imposes a new (additional) constraint on its vehicles. Where a wagon and/or locomotive is already rail track bound by its wheels, a train also is bound to the condition that the vehicles are attached to each other. The separate vehicles together form a whole 1 train.

The constraint added is that the distance between each coupling is limited by a min and max hook 'rope'. This is of course a simplification of the physical constraint for real world model trains (as hooks aren't ropes but rigid bodies), but it gives enough effect of a train composition because the vehicles are bound to the rail track anyway. As rail tracks won't make sudden abrupt 90-degree angles, but only slow smooth curves, the 'rope' constraint will give nice enough visual impression of inter-wagon connection.

Train Line System

While cars loosely follow road waypoints, trains drive strictly on railroad tracks. These railroad tracks are laid out within a TrainLineSystem. So the TrainLineSystem contains all pieces there are to the complete railway within the level.

In its simplest form, the TrainLineSystem is a collection of paths (or ways) connected at nodes.

Approximation of visually smooth track curves

The 3D Engines currently draws straight lines. Therefore a curve needs to be approximated by a number of smaller straight lines. See the following figure:

A line, a curve and a path
A (straight) line, a curve and a path approximation curves

The paths visually approximate smooth curves by concatenating small pieces of slightly rotated straight lines together.

Train Line

Each segment within the TrainLineSystem is called a TrainLine. The segment actually contains the 3D track mesh.

The segments are connected to each other via points (railroad switches / turnouts) or connectors.

A TrainLine is composed of track pieces
A TrainLine is composed of track pieces

Laying railroad track

Railroad track is layed very similar to the construction of a toy model railroad layout: consecutively by connected each train piece to the next. Railroad track is then layed using track piece chains. The order of the track laying must respect the natural driving direction. The following track pieces are available:

A starting track (for example a buffer stop) can be given a starting position and rotation. Any next connected track will be layed in the specified direction.

The slope parameter is expressed as a fraction. So when slope = 0.01 then the slope is 1 centimeter rise per 100 centimenter flat distance. Regardless of the slope the length is always measured in flat distance (does not take into account the equal or longer length of the slope distance). This makes connecting track pieces with slopes more easily in a 3D-world, as the distance is always measured on the 2D-surface plane.


Turnouts are logical units and don't have a track size (length) themselves, so you must connect straight or curved tracks yourself. All turnouts must have an unique id each so the track layer knows which tracks to connect where to it in what direction. Based on the entry and exit-points, tracks are connected to the turnouts. For example, the leftturnout has connecting points start, left and straight.

Power connectors

Power connectors are logical units and don't have a track size (length) themselves. You cannot start or end track with a powerconnector; it must be placed within a track segment. If a team is specified, the powerconnector becomes a possible starting location for any player in that team. If a player is specified, the powerconnector becomes the starting position for that player locomotive.

Trigger magnets

Trigger magnets are logical units and don't have a track size (length) themselves. You cannot start or end track with a triggermagnet; it must be placed within a track segment. If a group is specified, the triggermagnet will trigger any logic tied to that group. For example, you can open or close a bridge, a gated level crossing, change a signal, etc.


A crane is equiped with a hook and one or more controllable joints. The controllable joints allow for steering and operation of the crane.

Crane Structure Game Object properties
Crane Structure Game Object: accessors of attached objects

The joints can be restricted so that movement or rotation is limited to a certain distance or angle for example by setting the min and max properties.

Places & Zones

In the game, places and areas of interest and interaction are marked by the logical Place and Zone GameObjects. Both a Place and a Zone have a position and a rotation, but only a Zone has a scaling attribute.

The usage of places and areas enables interaction in the level for moveable GameObjects. For example for a Vehicle (including locomotives, wagons and figures), but also for Item (such as goods). Both a Place and a Zone trigger an action on moveable GameObjects entering and/or leaving the marked location or area.

A difference between a Place and a Zone is that an occupant bounding box need not fit in a Place, but must fit as a whole in a Zone.


A Place has no shape because it only marks a location. The rotation may indicate at which direction the occupant must occupy the place.

We defined a special set of Places that mark some standard location automatically. The special Place.type options are:


A Zone has either a box or sphere shape to describe the size of the area. The shape also determines the colission detection to use for GameObject models entering and exiting the area.

The default size of the Zone is Babylon.Vector3.One().

Use the GameObject parenting system to attach a Zone to a (often static) GameObject such as a Structure.

We defined a special set of Zones that accomplish some standard task automatically. These types of Zones also show visible markers, so their presense and usage is more clear to the Player. The special Zones are:

Level Setup

Players get to play Levels.

If you want to add a new Level to the list of playable levels, you will need to prepare a Level Setup Javascript file. This Level Setup file is loaded by the Level GameStateObject, when the Player chooses a Level.

The Level Setup file contains instructions for the Level-constructor about things like:

Steps in creating the Level Setup file

Design fun and challenging Levels can be an art in itself. Though these are the steps you need to take in general to design a Level Setup file:

  1. Draw key experiences you want to have for the Player on a notepad/sketch­board;

  2. Design the track layout in the right gauge/scale (optionally with the aid of track design software);

  3. Convert the designed track layout to the GeekTrains-format (Javascript);

  4. Add the most important GameStateObjects to the setup (more eye­candy to further dress-up the Level can be added at a later stage);

  5. Add objectives (winning/losing criteria);

  6. Playtest the level to see if the Level is indeed fun and challenging to play and really communicates the experience you want the Player to have;

  7. Add some more eye­candy to the level to complete the experience;

  8. Offer the Level to the lead developer for official publication;

The GeekTrains game uses the G-scale track gauge format of approximately 45mm (1¾ inch) by default.

Also larger 'big toy' / 'home garden' / 'fun parc' railroad scales are eligable to be played. For example 3½, 5, 7¼, 7½, 10¼, 12¼ and 15 inch gauge.

The code will scale all level scenery down such that the track becomes G-scale gauge again. So when placing scenery in a larger-than-G-scale-gauge Level, make sure you adjust the scaling-property of the GameStateObject to match the relative adjustment factor between your desired scale and the G-scale (e.g. 2 for converting 3½ to 1¾).

3rd Party Railway layout tools

You can design your railroad track layout using 3rd party tools from vendors like:

Using some simple logic, the designed track plan can be converted to the GeekTrains system. E.g. by approximating the length of straight track parts and the radius of curved track parts.

Elements of the Level Setup file (JavaScript)

level variable

First set the general properties of the level:

Property: level
iLevel number (higher level number means more complex level to complete)1
nameUnique internal level name'geektrainsdemo1'
titleA friendly title for this level, shown to player'GeekTrains Demo Level #1'
descriptionA longer description for this level, shown to player'A demo level of GeekTrains consisting of a basic circle railway track layout.'
authorName of the level author/creator'Conduqtor Q'
locationGeographical location of the level, for example a place name and a country name (especially used for outdoor track layouts)'Rotterdam, The Netherlands'
urlAn optional URL to a webpage containing more information on the level for background reading at another time''

world_config variable

Second, construct the empty world (that will later contain the model train tracks). It can have the following optional properties: (When you omit some properties, they will fall back to a default setting.)


The background to use (rendered behind the skybox, if a skybox is present).

The BABYLON.Color3 (or BABYLON.Color4) background color. For example BABYLON.Color3.Blue() (which is the default fallback color by the way).
URL to the background file, for example 'background.jpg'. If no URL is set, the background color will be visible.

The sky (rendered before background). Defaults to no sky (thus show background).


To enable a fog effect.

A BABYLON.Color3 color to use for as fog. Defaults to the generic clearColor.
A BABYLON.Scene.FOGMODE mode to use for as fog. Defaults to BABYLON.Scene.FOGMODE_NONE.
Adds a snowy cloud to the level. Optionally can make a hail storm.
Adds a rain cloud to the level. Optionally can make a thunder storm.
URL to the skybox JPEG file, without the file extension and dot. Will load the 6 face textures by adding a postfix per face to the URL (_nx, _ny, _nz, _px, _py, _pz). For example 'sunny'.

The surface plane (prevents objects from falling into an infinite abyss).

diffuseColor (same as: mainColor for grids)
Sets the diffuse BABYLON.Color3 for the surface (if no texture is specified).
emissiveColor (same as: lineColor for grids)
Sets the emissive BABYLON.Color3 for the surface (if no texture is specified).
Name of the texture within the texture library to use for the surface, for example 'dirt'. If no texture name is set, falls back to a diffuseColor material.
The type of surface, typically named after the Mesh to create. Choose from 'box', 'cylinder', 'disc', 'plane', 'tiledground'.
Property: world_config
terrainTextures for the terrain. 
heightmapOptional. Set it to give the terrain a heightmap. 
groundSets the size of the playground (where the train track is put upon). 

Optional. Configures the lighting for the scene.

You can set the type of lighting with type. Choose from: HemisphericLight (great for debugging), PointLight (great for indoor scenes), DirectionalLight (great for outdoor scenes) and SpotLight (great for mini-scenes). The scene will always be lit with at least a HemisphericLight.

You can optionally set the time with time. It can set the scene's lighting to a fixed time in day (midnight, night, dusk, morning, day, noon, afternoon, evening, dawn, night), or simulate a day/night schedule with setting clock (24 hours pass in 6 minutes, starts at day).

waterOptional. Adds a full-scale water to the level. 

Third, place the railway tracks:

track_line_layout (more info soon)

Fourth, add all other objects to complete the scene:

objectives variable

Finally, decide upon 1 and up to 5 mission objectives in order to complete the level. The objectives are stored as an Array, where each Item represents an Objective.

The Level is over when all objectives have been met, a critical objective has been failed, or when another non-objective event occurs like running out of health, fuel or money.

Property: objectives[]
iLevel number.0
objectiveShort title clearly describing the objective condition.'Arrive exactly at 12:00 at the Train Station'
compulsoryWhether the objective is required to be successfully met in order to complete the level.true
conditionA function that determines if the objective has been met (+1), failed (-1) or is still in progress (0).function () { return (<put your winning/losing condition here>); }