diff --git a/app/config.ctn b/app/config.ctn
index 8fca60d..555002e 100644
--- a/app/config.ctn
+++ b/app/config.ctn
@@ -8,4 +8,8 @@
 
 // defaults
 :window.default_widht: 1280
-:window.default_height: 720
\ No newline at end of file
+:window.default_height: 720
+
+// debug server
+:debug.server_ip: '127.0.0.1'
+:debug.server_port: 8145
\ No newline at end of file
diff --git a/app/core.ctn b/app/core.ctn
index 4df1821..623633c 100644
--- a/app/core.ctn
+++ b/app/core.ctn
@@ -30,4 +30,13 @@
 @shaders: VISU\Graphics\ShaderCollection(@GL, :visu.path.resources.shader)
     - enableVISUIncludes()
     - addVISUShaders()
-    - scanShaderDirectory(:visu.path.resources.shader)
\ No newline at end of file
+    - scanShaderDirectory(:visu.path.resources.shader)
+
+/**
+ * Debug server
+ */
+@debug.server: TowerDefense\Debug\DebugServer(
+    :debug.server_ip, 
+    :debug.server_port,
+    @visu.dispatcher
+)
\ No newline at end of file
diff --git a/bin/test-debug-client.php b/bin/test-debug-client.php
new file mode 100644
index 0000000..08dded6
--- /dev/null
+++ b/bin/test-debug-client.php
@@ -0,0 +1,52 @@
+<?php
+
+// create the client socket
+if (($sock = socket_create(AF_INET, SOCK_STREAM, SOL_TCP)) === false) {
+    echo "socket_create() failed: reason: " . socket_strerror(socket_last_error()) . "\n";
+}
+
+// connect to the server
+if (socket_connect($sock, '127.0.0.1', 8145) === false) {
+    echo "socket_connect() failed: reason: " . socket_strerror(socket_last_error()) . "\n";
+}
+
+$run = true;
+$msgCounter = 0;
+// client loop
+do {
+    echo "Command: ";
+
+    // read the command from the console
+    $stdin = fopen('php://stdin', 'r');
+
+    // prepare the json message
+    $msg = trim(fgets($stdin));
+    $request = [
+        'command' => $msg,
+        'request_id' => $msgCounter++
+    ];
+    if (stripos($msg, 'console_command') === 0) {
+        $request['command'] = 'console_command';
+        $request['console_command'] = trim(substr($msg, 16));
+    }
+    $msgJSON = json_encode($request);
+
+    echo "\nRequest:\n$msgJSON\n\n";
+
+    // send the message to the server
+    socket_write($sock, $msgJSON, strlen($msgJSON));
+
+    // error reading the response
+    if (false === ($buf = socket_read($sock, 16384))) {
+        echo "socket_read() failed, reason: " . socket_strerror(socket_last_error($sock)) . "\n";
+    }
+
+    echo "\nResponse:\n";
+    print_r(json_decode($buf, true));
+    echo "\n";
+
+    // check for disconnect
+    if ($msg == 'disconnect') {
+        $run = false;
+    }
+} while ($run);
diff --git a/composer.lock b/composer.lock
index b12c530..9fbcfb4 100644
--- a/composer.lock
+++ b/composer.lock
@@ -168,7 +168,7 @@
             "dist": {
                 "type": "path",
                 "url": "../visu",
-                "reference": "a62fa523d16f32d2a086f79bd2bb3c1d309ce99f"
+                "reference": "f7c215689198166a3a1f3beed1e1c769a413ac14"
             },
             "require": {
                 "clancats/container": "^1.3",
diff --git a/src/Debug/DebugServer.php b/src/Debug/DebugServer.php
new file mode 100644
index 0000000..d34840d
--- /dev/null
+++ b/src/Debug/DebugServer.php
@@ -0,0 +1,243 @@
+<?php
+
+namespace TowerDefense\Debug;
+
+use VISU\ECS\EntitiesInterface;
+use VISU\Geo\Transform;
+use VISU\Runtime\DebugConsole;
+use VISU\Signal\Dispatcher;
+use VISU\Signals\Runtime\ConsoleCommandSignal;
+
+/**
+ * Class DebugServer
+ * 
+ * @package TowerDefense\Debug
+ */
+class DebugServer
+{
+    private string $address = "127.0.0.1"; // localhost
+    private int $port = 8145; // default port
+    private ?object $socket = null; // the server socket
+    private array $clients = []; // the client sockets
+
+    private Dispatcher $dispatcher; // the signal dispatcher
+    private ?DebugConsole $console = null; // the optional debug console
+
+    /**
+     * DebugServer constructor.
+     * 
+     * @param string $address 
+     * @param int $port 
+     * @return void 
+     */
+    public function __construct(string $address, int $port, Dispatcher $dispatcher)
+    {
+        $this->address = $address;
+        $this->port = $port;
+        $this->dispatcher = $dispatcher;
+    }
+
+    public function setDebugConsole(DebugConsole $console): void
+    {
+        $this->console = $console;
+    }
+
+    /**
+     * Start the debug server
+     * @return void 
+     */
+    public function start(): void
+    {
+        // create the server socket
+        $this->socket = socket_create(AF_INET, SOCK_STREAM, 0);
+
+        // bind the socket to the address and port
+        if (!socket_bind($this->socket, $this->address, $this->port)) {
+            // close the server socket
+            socket_close($this->socket);
+            $this->socket = null;
+            echo "DebugServer: Could not bind to address " . $this->address . " on port " . $this->port;
+            return;
+        }
+
+        // start listening for connections
+        socket_listen($this->socket);
+
+        // set the socket to non-blocking
+        socket_set_nonblock($this->socket);
+    }
+
+    /**
+     * Stop the debug server
+     * 
+     * @return void 
+     */
+    public function stop(): void
+    {
+        // close all clients
+        foreach ($this->clients as $i => $client) {
+            socket_close($client);
+            unset($this->clients[$i]);
+        }
+
+        // close the server socket
+        if ($this->socket !== null) {
+            socket_close($this->socket);
+        }
+    }
+
+    /**
+     * Process the debug server input and output
+     * 
+     * @return void 
+     */
+    public function process(EntitiesInterface $entities): void
+    {
+        if ($this->socket == null) {
+            return;
+        }
+
+        // check for new connections
+        if ($newsock = socket_accept($this->socket)) {
+            socket_set_nonblock($newsock);
+            echo "New client connected\n";
+            $this->clients[] = $newsock;
+        }
+
+        // check for existing clients
+        if (count($this->clients) < 1) {
+            return;
+        }
+
+        $readingFromClients = $this->clients;
+
+        // check for input
+        $changed = socket_select($readingFromClients, $w, $e, 0);
+
+        if (false == $changed) {
+            return;
+        }
+
+        if ($changed < 1) {
+            return;
+        }
+
+        // process input
+        foreach ($readingFromClients as $i => $socketItem) {
+            if ($input = socket_read($socketItem, 16384)) {
+                $input = trim($input);
+
+                echo "Client $i: $input\n";
+
+                $response = $this->handleInput($input, $entities);
+                $responseJSON = json_encode($response);
+                socket_write($socketItem, $responseJSON, strlen($responseJSON));
+
+                // check for disconnect
+                if (isset($response['request']) && $response['request'] == "disconnect") {
+                    socket_close($socketItem);
+                    unset($this->clients[$i]);
+                }
+            } else {
+                // most probably the socket was closed from the client side
+                echo "Socket read error: " . socket_strerror(socket_last_error()) . "\n";
+                socket_close($socketItem);
+                unset($this->clients[$i]);
+            }
+        }
+    }
+
+    /**
+     * Handle the input command from the client
+     * 
+     * @param string $input 
+     * @param EntitiesInterface $entities 
+     * @return array 
+     */
+    private function handleInput(string $input, EntitiesInterface $entities): array
+    {
+        // parse the input
+        $inputMessageJSON = json_decode($input, true);
+        if ($inputMessageJSON == null) {
+            // invalid JSON
+            return [
+                'error' => "Invalid JSON on input"
+            ];
+        }
+
+        // check for the command
+        if (!isset($inputMessageJSON['command'])) {
+            return [
+                'error' => "Missing command"
+            ];
+        }
+
+        // check for the request id
+        if (!isset($inputMessageJSON['request_id'])) {
+            return [
+                'error' => "Missing request id for response"
+            ];
+        }
+
+        // prepare the response
+        $response = [
+            'response_id' => $inputMessageJSON['request_id'],
+            'request' => $inputMessageJSON['command']
+        ];
+
+        // handle the command
+        switch ($inputMessageJSON['command']) {
+            case 'disconnect':
+                // close the socket
+                $response['response'] = "Bye!";
+                break;
+            case 'available_commands':
+                // return the available commands
+                $response['response'] = [
+                    "disconnect",
+                    "available_commands",
+                    "list_transform_components",
+                    "console_command"
+                ];
+                break;
+            case 'list_transform_components':
+                // return the list of transform components, their connected entity ids and their positions
+                $transformComponents = [];
+                foreach ($entities->view(Transform::class) as $entity => $transform) {
+                    $transformComponents[] = [
+                        'entity_id' => $entity,
+                        'position' => [
+                            'x' => $transform->position->x,
+                            'y' => $transform->position->y,
+                            'z' => $transform->position->z
+                        ]
+                    ];
+                }
+                $response['response'] = $transformComponents;
+                break;
+            case 'console_command':
+                // trigger a console command
+                if ($this->console == null) {
+                    // debug console not available
+                    $response['error'] = "Debug console not available";
+                    break;
+                }
+                if (!isset($inputMessageJSON['console_command']) || strlen($inputMessageJSON['console_command']) < 1) {
+                    // missing console command
+                    $response['error'] = "Missing console command";
+                    break;
+                }
+                // dispatch the console command signal
+                $commandSignal = new ConsoleCommandSignal($inputMessageJSON['console_command'], $this->console);
+                $this->dispatcher->dispatch(DebugConsole::EVENT_CONSOLE_COMMAND, $commandSignal);
+                $response['response'] = "Console command triggered";
+                break;
+            default:
+                // unknown command
+                $response['error'] = "Unknown command";
+                break;
+        }
+
+        return $response;
+    }
+}
diff --git a/src/Game.php b/src/Game.php
index 41e908b..17e5223 100644
--- a/src/Game.php
+++ b/src/Game.php
@@ -103,6 +103,11 @@ public function __construct(GameContainer $container)
         // initialize the debug renderers
         $this->dbgText = new DebugTextOverlay($container);
         $this->dbgConsole = new DebugConsole($container->resolveGL(), $container->resolveInput(), $container->resolveVisuDispatcher());
+        if ($this->container->get('debug.server')) {
+            // set the debug console on the debug server if it is available
+            $debugServer = $this->container->get('debug.server');
+            $debugServer->setDebugConsole($this->dbgConsole);
+        }
         $this->dbg3D = new Debug3DRenderer($container->resolveGL());
         Debug3DRenderer::setGlobalInstance($this->dbg3D);
 
diff --git a/src/Scene/LevelEditorScene.php b/src/Scene/LevelEditorScene.php
index afb82f1..f362197 100644
--- a/src/Scene/LevelEditorScene.php
+++ b/src/Scene/LevelEditorScene.php
@@ -31,7 +31,7 @@ class LevelEditorScene extends LevelScene
     /**
      * Returns the scenes name
      */
-    public function getName() : string
+    public function getName(): string
     {
         return 'TowerDefense Level [EDITOR]';
     }
@@ -41,8 +41,7 @@ public function getName() : string
      */
     public function __construct(
         protected GameContainer $container,
-    )
-    {
+    ) {
         parent::__construct($container);
 
         // enable the dev picker
@@ -64,10 +63,8 @@ public function __construct(
             $this->gizmoEditorSystem,
         ]);
 
-
         // listen for editor signals
-        $this->container->resolveVisuDispatcher()->register(DebugConsole::EVENT_CONSOLE_COMMAND, function(ConsoleCommandSignal $signal) 
-        {
+        $this->container->resolveVisuDispatcher()->register(DebugConsole::EVENT_CONSOLE_COMMAND, function (ConsoleCommandSignal $signal) {
             if (!$signal->isAction('editor.spawn')) {
                 return;
             }
@@ -87,9 +84,24 @@ public function __construct(
             $renderable->model = $this->loadedObjects[$objName]; // <- render the turret
             $transform = $this->entities->attach($newObj, new Transform);
             $transform->scale = $transform->scale * 10;
-           
+
             $this->selectedEntity = $newObj;
         });
+
+        // start debug server
+        if ($this->container->get('debug.server')) {
+            $debugServer = $this->container->get('debug.server');
+            $debugServer->start();
+        }
+    }
+
+    public function __destruct()
+    {
+        // stop debug server
+        if ($this->container->get('debug.server')) {
+            $debugServer = $this->container->get('debug.server');
+            $debugServer->stop();
+        }
     }
 
     /**
@@ -98,7 +110,7 @@ public function __construct(
      * 
      * @return void 
      */
-    public function load() : void
+    public function load(): void
     {
         parent::load();
 
@@ -112,22 +124,30 @@ public function load() : void
      * This is the place where you should update your game state.
      * @return void 
      */
-    public function update() : void
+    public function update(): void
     {
         parent::update();
 
+        // process debug server
+        if ($this->container->get('debug.server')) {
+            $debugServer = $this->container->get('debug.server');
+            $debugServer->process($this->entities);
+        }
+
+        // update systems
+        $this->gizmoEditorSystem->update($this->entities);
+
         $heightmapComponent = $this->entities->getSingleton(HeightmapComponent::class);
 
         // get input context
         $inputContext = $this->container->resolveInputContext();
-        
+
         // always enable raycasting
         $heightmapComponent->enableRaycasting = true;
 
         // if an entity is selected and has a transform component
         // we update the position until the user clicks the mouse button again
-        if ($this->entities->valid($this->selectedEntity) && $this->entities->has($this->selectedEntity, Transform::class)) 
-        {
+        if ($this->entities->valid($this->selectedEntity) && $this->entities->has($this->selectedEntity, Transform::class)) {
             // if an entity is selected ensure it has a gizmo attached to it 
             if (!$this->entities->has($this->selectedEntity, GizmoComponent::class)) {
                 $this->entities->attach($this->selectedEntity, new GizmoComponent);
@@ -160,7 +180,7 @@ public function update() : void
             // copy
             if ($inputContext->actions->didButtonRelease('copy')) {
                 $copy = $this->entities->create();
-                foreach($this->entities->components($this->selectedEntity) as $component) {
+                foreach ($this->entities->components($this->selectedEntity) as $component) {
                     $this->entities->attach($copy, clone $component);
                 }
                 $this->selectedEntity = $copy;
@@ -176,8 +196,7 @@ public function update() : void
             if ($inputContext->actions->didButtonRelease('place')) {
                 $this->deselectEntity();
             }
-        }
-        else {
+        } else {
             $this->deselectEntity();
         }
 
@@ -191,7 +210,7 @@ public function update() : void
      * 
      * @param RenderContext $context
      */
-    public function render(RenderContext $context) : void
+    public function render(RenderContext $context): void
     {
         parent::render($context);
 
@@ -234,8 +253,7 @@ public function render(RenderContext $context) : void
 
         // hightlight the selected entity
         // by rendering an AABB around it
-        if ($this->entities->valid($this->selectedEntity)) 
-        {
+        if ($this->entities->valid($this->selectedEntity)) {
             DebugTextOverlay::debugString("Entity {$this->selectedEntity} selected");
             DebugTextOverlay::debugString(" - Rotate: press '.' & ','");
             DebugTextOverlay::debugString(" - Follow Terrain: hold 'f'");
@@ -244,13 +262,13 @@ public function render(RenderContext $context) : void
             DebugTextOverlay::debugString(" - Delete: press 'backspace'");
 
             if (
-                $this->entities->has($this->selectedEntity, DynamicRenderableModel::class) && 
+                $this->entities->has($this->selectedEntity, DynamicRenderableModel::class) &&
                 $this->entities->has($this->selectedEntity, Transform::class)
             ) {
                 $renderable = $this->entities->get($this->selectedEntity, DynamicRenderableModel::class);
                 $transform = $this->entities->get($this->selectedEntity, Transform::class);
 
-                foreach($renderable->model->meshes as $mesh) {
+                foreach ($renderable->model->meshes as $mesh) {
                     D3D::aabb($transform->position, $mesh->aabb->min * $transform->scale, $mesh->aabb->max * $transform->scale, D3D::$colorGreen);
                 }
             }
@@ -261,7 +279,7 @@ private function deselectEntity()
     {
         // remove gizmo components from the selected one
         $this->entities->detach($this->selectedEntity, GizmoComponent::class);
-        
+
         $this->selectedEntity = 0;
         $this->devPicker->enabled = true;
         $this->container->resolveInputContext()->switchToNext('level_editor');
@@ -289,8 +307,8 @@ public function devEntityPickerDidSelectEntity(int $entityId): void
             $this->deselectEntity();
             return;
         }
-        
+
         echo "Entity selected: $entityId\n";
         $this->selectEntity($entityId);
     }
-}
\ No newline at end of file
+}
diff --git a/src/Scene/LevelScene.php b/src/Scene/LevelScene.php
index 123d25f..48c0f4f 100644
--- a/src/Scene/LevelScene.php
+++ b/src/Scene/LevelScene.php
@@ -3,9 +3,13 @@
 namespace TowerDefense\Scene;
 
 use GameContainer;
+use GL\Math\GLM;
+use GL\Math\Quat;
+use GL\Math\Vec3;
 use TowerDefense\Debug\DebugTextOverlay;
 use TowerDefense\Renderer\RoadRenderer;
 use TowerDefense\Renderer\TerrainRenderer;
+use TowerDefense\System\AircraftSystem;
 use TowerDefense\System\CameraSystem;
 use TowerDefense\System\HeightmapSystem;
 use VISU\Component\VISULowPoly\DynamicRenderableModel;
@@ -23,6 +27,7 @@
 use VISU\System\VISULowPoly\LPModel;
 use VISU\System\VISULowPoly\LPObjLoader;
 use VISU\System\VISULowPoly\LPRenderingSystem as VISULowPolyRenderingSystem;
+use VISU\System\VISUTransitionAnimationSystem;
 
 abstract class LevelScene extends BaseScene implements DevEntityPickerDelegate
 {
@@ -44,6 +49,8 @@ abstract class LevelScene extends BaseScene implements DevEntityPickerDelegate
     private VISULowPolyRenderingSystem $renderingSystem;
     private CameraSystem $cameraSystem;
     private HeightmapSystem $heightmapSystem;
+    private AircraftSystem $aircraftSystem;
+    private VISUTransitionAnimationSystem $animationSystem;
 
     /**
      * Object loader (3D models)
@@ -73,11 +80,10 @@ abstract class LevelScene extends BaseScene implements DevEntityPickerDelegate
      */
     public function __construct(
         protected GameContainer $container,
-    )
-    {
+    ) {
         parent::__construct($container);
 
-        // register key handler for debugging 
+        // register key handler for debugging
         // usally a system should handle this but this is temporary
         $this->keyboardHandlerId = $this->container->resolveVisuDispatcher()->register('input.key', [$this, 'handleKeyboardEvent']);
 
@@ -85,7 +91,7 @@ public function __construct(
         $this->objectLoader = new LPObjLoader($container->resolveGL());
         $this->loadedObjects = $this->objectLoader->loadAllInDirectory(VISU_PATH_RESOURCES . '/models/spacekit');
 
-        // prepare the rendering systems 
+        // prepare the rendering systems
         $this->terrainRenderer = new TerrainRenderer($container->resolveGL(), $container->resolveShaders());
         $this->roadRenderer = new RoadRenderer($container->resolveGL(), $container->resolveShaders());
         $this->renderingSystem = new VISULowPolyRenderingSystem($container->resolveGL(), $container->resolveShaders());
@@ -99,7 +105,7 @@ public function __construct(
             $this->container->resolveInputContext(),
             $this->container->resolveVisuDispatcher()
         );
-        
+
         // heightmap system (this is not the terrain)
         $this->heightmapSystem = new HeightmapSystem(
             $this->container->resolveGL(),
@@ -109,11 +115,17 @@ public function __construct(
             ]
         );
 
+        $this->animationSystem = new VISUTransitionAnimationSystem(60); // @TODO get from game loop
+        $this->aircraftSystem = new AircraftSystem($this->loadedObjects, $this->entities, $this->container->resolveVisuDispatcher(), $this->container->resolveInput());
+        $this->animationSystem->addAnimationDelegate($this->aircraftSystem);
+
         // bind all systems to the scene itself
         $this->bindSystems([
             $this->cameraSystem,
             $this->renderingSystem,
             $this->heightmapSystem,
+            $this->animationSystem,
+            $this->aircraftSystem
         ]);
 
         // construct the dev entity picker
@@ -135,20 +147,20 @@ public function __destruct()
     /**
      * Constrcuts the DEV entity picker
      */
-    public function constructDevEntityPicker() : void
+    public function constructDevEntityPicker(): void
     {
         // right now using the window framebuffer as render target
         // for entity picking, we need to test if is more efficient
-        // to use a separate framebuffer for this, that could have a much 
+        // to use a separate framebuffer for this, that could have a much
         // lower resolution
         $this->devPickerRenderTarget = $this->container->resolveWindowMain()->getRenderTarget();
 
         // not to happy with the dependency on the rendering system here
-        // but I tried a few architectures now and did not want to waste 
+        // but I tried a few architectures now and did not want to waste
         // more time on this...
         $this->devPicker = new DevEntityPicker(
             $this,
-            $this->entities, 
+            $this->entities,
             $this->container->resolveVisuDispatcher(),
             $this->devPickerRenderTarget,
             [
@@ -167,7 +179,7 @@ public function constructDevEntityPicker() : void
      * 
      * @return void 
      */
-    public function load() : void
+    public function load(): void
     {
         // load the terrain
         $this->terrainRenderer->loadTerrainFromObj(VISU_PATH_RESOURCES . '/terrain/alien_planet/alien_planet_terrain.obj');
@@ -190,8 +202,8 @@ private function prepareScene()
         // create a camera
         $cameraEntity = $this->entities->create();
         $camera = $this->entities->attach($cameraEntity, new Camera(CameraProjectionMode::perspective));
-        $camera->transform->position->y = 100;
-        $camera->transform->position->z = 50;
+        $camera->transform->position->y = 250;
+        $camera->transform->position->z = 500;
         $this->cameraSystem->setActiveCameraEntity($cameraEntity);
 
         // create some random renderable objects
@@ -212,8 +224,13 @@ private function prepareScene()
         $transform->position->y = 0;
         $transform->position->x = 10;
         $transform->position->z = 10;
-
         $transform->setParent($this->entities, $someObject);   
+
+        // add aircraft
+        $initialPosition = new Vec3(0.0, 250.0, -250.0);
+        $initialOrientation = new Quat();
+        $initialOrientation->rotate(GLM::radians(-90.0), new Vec3(0.0, 1.0, 0.0));
+        $this->aircraftSystem->spawnAircraft($this->entities, $initialPosition->copy(), $initialOrientation->copy());
     }
 
     /**
@@ -222,9 +239,11 @@ private function prepareScene()
      * 
      * @return void 
      */
-    public function update() : void
+    public function update(): void
     {
         $this->cameraSystem->update($this->entities);
+        $this->aircraftSystem->update($this->entities);
+        $this->animationSystem->update($this->entities);
         $this->heightmapSystem->update($this->entities);
     }
 
@@ -234,10 +253,14 @@ public function update() : void
      * 
      * @param RenderContext $context
      */
-    public function render(RenderContext $context) : void
+    public function render(RenderContext $context): void
     {
         DebugTextOverlay::debugString("Press 'SHIFT' + NUM for render debug: NONE=0, POS=1, VPOS=2, NORM=3, DEPTH=4, ALBEDO=5");
 
+        $this->aircraftSystem->render($this->entities, $context);
+
+        $this->animationSystem->render($this->entities, $context);
+
         $this->cameraSystem->render($this->entities, $context);
         $this->heightmapSystem->render($this->entities, $context);
 
@@ -250,7 +273,7 @@ public function render(RenderContext $context) : void
     /**
      * Keyboard event handler
      */
-    public function handleKeyboardEvent(KeySignal $signal) : void
+    public function handleKeyboardEvent(KeySignal $signal): void
     {
         if ($signal->mods !== Key::MOD_SHIFT && $signal->action == GLFW_PRESS) {
             return;
@@ -270,23 +293,23 @@ public function handleKeyboardEvent(KeySignal $signal) : void
             $this->renderingSystem->debugMode = VISULowPolyRenderingSystem::DEBUG_MODE_ALBEDO;
         } elseif ($signal->key === KEY::NUM_6) {
             $this->renderingSystem->debugMode = VISULowPolyRenderingSystem::DEBUG_MODE_SSAO;
-        } 
+        }
     }
 
     /**
      * Called when the dev entity picker has selected an entity
-     * 
-     * @param int $entityId 
-     * @return void 
+     *
+     * @param int $entityId
+     * @return void
      */
     public function devEntityPickerDidSelectEntity(int $entityId): void
     {
     }
 
     /**
-     * Called when the dev entity picker is about to initate a selection and requires 
+     * Called when the dev entity picker is about to initate a selection and requires
      * the delegate to return the current camera data
-     * 
+     *
      * @return CameraData
      */
     public function devEntityPickerRequestsCameraData(): CameraData
diff --git a/src/System/AircraftSystem.php b/src/System/AircraftSystem.php
new file mode 100644
index 0000000..b0cd827
--- /dev/null
+++ b/src/System/AircraftSystem.php
@@ -0,0 +1,223 @@
+<?php
+
+namespace TowerDefense\System;
+
+use GL\Math\GLM;
+use GL\Math\Quat;
+use GL\Math\Vec3;
+use TowerDefense\Component\HeightmapComponent;
+use VISU\Animation\Transition\AnimationEasingType;
+use VISU\Animation\Transition\AnimationSequence;
+use VISU\Animation\Transition\AnimationUtil;
+use VISU\Animation\Transition\BaseAnimation;
+use VISU\Animation\Transition\BaseAnimationContainer;
+use VISU\Animation\Transition\TransformOrientationAnimation;
+use VISU\Animation\Transition\TransformPositionAnimation;
+use VISU\Animation\Transition\TransformScaleAnimation;
+use VISU\Component\AnimationComponent;
+use VISU\Component\VISULowPoly\DynamicRenderableModel;
+use VISU\ECS\EntitiesInterface;
+use VISU\ECS\SystemInterface;
+use VISU\Geo\Math;
+use VISU\Geo\Transform;
+use VISU\Graphics\Rendering\RenderContext;
+use VISU\OS\Input;
+use VISU\Signal\Dispatcher;
+use VISU\Signals\Input\MouseClickSignal;
+use VISU\System\VISUTransitionAnimationSystemDelegate;
+
+class AircraftSystem implements SystemInterface, VISUTransitionAnimationSystemDelegate
+{
+    /**
+     * @var int[] $aircrafts The list of aircraft entity ids
+     */
+    private array $aircrafts = [];
+
+    /**
+     * Function ID for mouse click handler
+     */
+    private int $mouseClickHandlerId = 0;
+
+    /**
+     * Constructor
+     *
+     * @param array $loadedObjects
+     *
+     */
+    public function __construct(
+        private array &$loadedObjects,
+        private EntitiesInterface &$entities,
+        private Dispatcher $dispatcher,
+        private Input $input
+    ) {
+        $this->mouseClickHandlerId = $this->dispatcher->register('input.mouse_click', [$this, 'handleMouseClickEvent']);
+    }
+
+    public function spawnAircraft(EntitiesInterface $entities, Vec3 $initialPosition, Quat $initialOrientation): void
+    {
+        $newAircraft = $entities->create();
+        $this->aircrafts[] = $newAircraft;
+
+        $renderable = $entities->attach($newAircraft, new DynamicRenderableModel);
+        $renderable->model = $this->loadedObjects['craft_speederA.obj'];
+
+        $transform = $entities->attach($newAircraft, new Transform);
+        $transform->scale = $transform->scale * 50;
+        $transform->position = $initialPosition;
+        $transform->orientation = $initialOrientation;
+        $transform->isDirty = true;
+
+        $animationComponent = $entities->attach($newAircraft, new AnimationComponent());
+        $orientation = new Quat();
+        $orientation->rotate(GLM::radians(-90.0), new Vec3(0.0, 1.0, 0.0));
+        /*$animationComponent->animation = new AnimationSequence([
+            new TransformPositionAnimation(new Vec3(-500.0, -200.0, 0.0), 2000, AnimationEasingType::EASE_IN_OUT),
+            new ParallelAnimations([
+                new TransformPositionAnimation(new Vec3(0.0, 500.0, 0.0), 2000, AnimationEasingType::EASE_IN_OUT),
+                new AnimationSequence([
+                    new TransformScaleAnimation(new Vec3(0.5, 0.5, 0.5), 500),
+                    new TransformScaleAnimation(new Vec3(2.0, 2.0, 2.0), 500)
+                ]),
+                new TransformOrientationAnimation($orientation, 1000, AnimationEasingType::EASE_IN_OUT)
+            ]),
+            new TransformPositionAnimation(new Vec3(500.0, 0.0, 0.0), 2000, AnimationEasingType::EASE_IN_OUT),
+        ]);*/
+
+        $animationComponent->animation = new AnimationSequence([
+            new TransformOrientationAnimation($orientation, 1000, AnimationEasingType::EASE_IN_OUT, initialDelay: 0, repeat: true, repeatCount: 4, repeatDelay: 1000, reverse: true, reverseCount: 1, reverseDelay: 250),
+            new TransformPositionAnimation(new Vec3(-500.0, -200.0, 0.0), 2000, AnimationEasingType::EASE_IN_OUT, initialDelay: 500, repeat: true, repeatCount: 1, repeatDelay: 500, reverse: true, reverseCount: 1, reverseDelay: 500),
+            new TransformScaleAnimation(new Vec3(0.5, 0.5, 0.5), 1000, AnimationEasingType::EASE_IN_OUT, repeat: true, repeatCount: 3, reverse: true, reverseCount: 4)
+        ]);
+    }
+
+    /**
+     * Mouse click event handler
+     */
+    public function handleMouseClickEvent(MouseClickSignal $signal)
+    {
+        $p = $this->input->getNormalizedCursorPosition();
+        $heightmapComponent = $this->entities->getSingleton(HeightmapComponent::class);
+        $o = $heightmapComponent->cursorWorldPosition;
+
+        echo "Mouse click event native: x " . $signal->position->x . " y " . $signal->position->y . PHP_EOL;
+        echo "Mouse click event normalized: x " . $p->x . " y " . $p->y . PHP_EOL;
+        echo "Mouse click event heightmap based: x " . $o->x . " y " . $o->y . " z " . $o->z . PHP_EOL . PHP_EOL;
+
+        // forward the click to the aircraft system
+        $this->clickAt($o);
+    }
+
+    /**
+     * Handle a click event
+     *
+     * @param EntitiesInterface $entities
+     * @param Vec3 $position
+     * @return void
+     */
+    private function clickAt(Vec3 $position): void
+    {
+        // get the first aircraft
+        $aircraft = $this->aircrafts[0];
+
+        // get the transform component
+        $transform = $this->entities->get($aircraft, Transform::class);
+
+        // set the new rotated orientation
+        $height = $position->y;
+        $position->y = $transform->position->y; // lock height to current height
+        $targetOrientation = Math::quatFacingForwardTowardsTarget($transform->position, $position, Transform::worldBackward(), Transform::worldUp());
+
+        // set the target height of the terrain + 10 "meters"
+        $position->y = $height + 10.0;
+
+        if (!$this->entities->has($aircraft, AnimationComponent::class)) {
+            $animationComponent = $this->entities->attach($aircraft, new AnimationComponent());
+        } else {
+            $animationComponent = $this->entities->get($aircraft, AnimationComponent::class);
+        }
+
+        $orientationAnimation = TransformOrientationAnimation::fromCurrentAndTargetOrientation($transform->orientation, $targetOrientation, 500);
+        $orientationAnimation->easingType = AnimationEasingType::EASE_IN_OUT;
+        $orientationAnimation->duration = AnimationUtil::calculateOrientationTransitionDuration($orientationAnimation->modifier, 360, 250);
+
+        $positionAnimation = TransformPositionAnimation::fromCurrentAndTargetPosition($transform->position, $position, 1000);
+        $positionAnimation->easingType = AnimationEasingType::EASE_IN_OUT;
+        $positionAnimation->duration = AnimationUtil::calculatePositionTransitionDuration($positionAnimation->modifier, 25, 500);
+
+        $animationComponent->animation = new AnimationSequence([$orientationAnimation, $positionAnimation]);
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function register(EntitiesInterface $entities): void
+    {
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function unregister(EntitiesInterface $entities): void
+    {
+        $this->dispatcher->unregister('input.mouse_click', $this->mouseClickHandlerId);
+
+        foreach ($this->aircrafts as $aircraft) {
+            $entities->destroy($aircraft);
+        }
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function update(EntitiesInterface $entities): void
+    {
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function render(EntitiesInterface $entities, RenderContext $context): void
+    {
+        // TODO: Implement render() method.
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function animationDidStart(BaseAnimation $animation, int $entity, bool $reversing, bool $repeating, bool $waiting): void
+    {
+        // TODO: Implement animationDidStart() method.
+        echo "Animation started: " . get_class($animation) . PHP_EOL;
+        echo "Reversing: " . ($reversing ? 'true' : 'false') . PHP_EOL;
+        echo "Repeating: " . ($repeating ? 'true' : 'false') . PHP_EOL;
+        echo "Waiting: " . ($waiting ? 'true' : 'false') . PHP_EOL . PHP_EOL;
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function animationDidStop(BaseAnimation $animation, int $entity, bool $finished): void
+    {
+        // TODO: Implement animationDidStop() method.
+        echo "Animation stopped: " . get_class($animation) . PHP_EOL;
+        echo "Finished: " . ($finished ? 'true' : 'false') . PHP_EOL . PHP_EOL;
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function animationGroupDidBegin(BaseAnimationContainer $animationGroup, int $entity): void
+    {
+        // TODO: Implement animationGroupDidBegin() method.
+        echo "Animation group began: " . get_class($animationGroup) . PHP_EOL . PHP_EOL;
+    }
+
+    /**
+     * @inheritDoc
+     */
+    public function animationGroupDidFinish(BaseAnimationContainer $animationGroup, int $entity): void
+    {
+        // TODO: Implement animationGroupDidFinish() method.
+        echo "Animation group finished: " . get_class($animationGroup) . PHP_EOL . PHP_EOL;
+    }
+}
diff --git a/src/System/HeightmapSystem.php b/src/System/HeightmapSystem.php
index 576e4a5..e578d75 100644
--- a/src/System/HeightmapSystem.php
+++ b/src/System/HeightmapSystem.php
@@ -7,6 +7,7 @@
 use VISU\ECS\SystemInterface;
 use VISU\Graphics\Camera;
 use VISU\Graphics\GLState;
+use VISU\Graphics\Heightmap\GPUHeightmapGeometryPassInterface;
 use VISU\Graphics\Heightmap\GPUHeightmapRenderer;
 use VISU\Graphics\Rendering\Pass\BackbufferData;
 use VISU\Graphics\Rendering\RenderContext;
