Nimbin[12]?SDL & Graphics / sdl_runtime_compiler / commits / 5a06cb2

sdl_runtime_compiler git · main

SDL3 game for running and compiling code at runtime

sdl3 c++ compiler dlopen cmake · first commit 2026-04-19 · last commit 2026-07-03 (3 months ago) · synced 3 days ago · upstream: git.ide3.de/hsnr/sdl-runtime-compiler

C++ 72.3% C 26.2%
git clone https://git.christianimmanuel.de/sdl-graphics/sdl_runtime_compiler.gitwget https://git.christianimmanuel.de/sdl-graphics/sdl_runtime_compiler/archive/sdl_runtime_compiler.tar.gz

Using DynArray

Christian Immanuel · 2026-05-31 10:01 · 5a06cb27538070f1abe7850d57b132d69d8612ef

 assets/sandbox.c                   | 20 +++++++++----------
 src/c_token/token.hpp              |  1 -
 src/c_token/token_db.cpp           | 15 +++++++-------
 src/c_token/token_db.hpp           | 10 ++++++----
 src/entities/player.hpp            |  1 -
 src/entities/pult.hpp              |  4 ++--
 src/entities/world.cpp             |  2 +-
 src/levels/goal.cpp                |  4 ++--
 src/levels/goal.hpp                | 14 ++++++-------
 src/levels/level.hpp               |  8 ++++----
 src/levels/level_list.cpp          |  2 +-
 src/levels/level_list.hpp          |  2 +-
 src/levels/level_runner.cpp        |  4 ++--
 src/levels/level_runner.hpp        |  2 +-
 src/levels/scene.hpp               | 10 +++++-----
 src/rendering/floor_mandelbrot.hpp |  6 +++---
 src/rendering/gl_renderer.cpp      |  8 ++++----
 src/rendering/gl_renderer.hpp      |  2 +-
 src/states/game_context.hpp        |  8 ++++----
 src/states/game_state.cpp          | 40 +++++++++++++++++++-------------------
 src/states/game_state.hpp          | 29 +++++++++++++--------------
 src/states/menu_state.hpp          |  4 ++--
 src/systems/carry_system.cpp       | 10 +++++-----
 src/systems/carry_system.hpp       |  8 ++++----
 src/systems/compiler_system.hpp    |  3 ++-
 src/systems/input_system.hpp       |  5 +++--
 src/systems/mod_loader.cpp         |  4 ++--
 src/systems/mod_loader.hpp         |  4 ++--
 src/systems/particle_system.cpp    |  2 +-
 src/systems/particle_system.hpp    |  6 +++---
 src/systems/physics_system.cpp     | 22 ++++++++++-----------
 src/systems/physics_system.hpp     | 25 ++++++++++++------------
 src/systems/pick_system.cpp        |  6 +++---
 src/systems/pick_system.hpp        | 11 ++++++-----
 src/systems/plate_system.cpp       |  4 ++--
 src/systems/plate_system.hpp       |  6 ++----
 src/systems/projectile_system.cpp  | 24 +++++++++++------------
 src/systems/projectile_system.hpp  | 16 +++++++--------
 src/systems/pult_system.cpp        | 12 ++++++------
 src/systems/pult_system.hpp        | 18 ++++++++---------
 src/systems/source_tokens.hpp      | 15 +++++++-------
 src/systems/word_ai_system.cpp     | 20 +++++++++----------
 src/systems/word_ai_system.hpp     | 18 ++++++++---------
 src/systems/word_anim.cpp          |  6 +++---
 src/systems/word_anim.hpp          | 11 ++++++-----
 src/text_3d/text_3d.cpp            |  3 +--
 src/text_3d/text_3d.hpp            |  9 ++++-----
 src/text_3d/text_label.cpp         |  8 ++++----
 src/text_3d/text_label.hpp         | 14 ++++++-------
 src/text_3d/text_mesh.cpp          | 20 +++++++++----------
 src/text_3d/text_mesh.hpp          | 12 +++++++-----
 src/thirdparty/earcut/earcut.hpp   | 15 +++++++-------
 src/utils/carry_math.cpp           |  6 +++---
 src/utils/carry_math.hpp           |  8 +++++---
 src/utils/globals.hpp              | 11 +++++++----
 src/utils/text_wrap.hpp            |  8 +++++---
 src/wall/editor_wall.cpp           |  4 ++--
 src/wall/editor_wall.hpp           |  7 +++----
 src/wall/text_wall.cpp             |  6 +++---
 src/wall/text_wall_scroll.hpp      |  2 +-
 src/wall/wall_surfaces.hpp         |  8 ++++----
 61 files changed, 301 insertions(+), 292 deletions(-)

diff --git a/assets/sandbox.c b/assets/sandbox.c
index 00fa869..aa2846b 100644
--- a/assets/sandbox.c
+++ b/assets/sandbox.c
@@ -2,14 +2,14 @@
 static int n ;
 static ModObject * o ;
 static void box ( double x , double y , double z , double w , double h , double d , int dyn ) {
-    o[n].kind = MOD_BOX; o[n].dynamic = dyn;
-    double hx = w * 0.5, hy = h * 0.5, hz = d * 0.5;
-    o[n].x = x;
-    o[n].y = y + hy;
-    o[n].z = z;
-    o[n].hx = hx; o[n].hy = hy; o[n].hz = hz;
-    o[n].r = 0.6; o[n].g = 0.6; o[n].b = 0.8;
-    n = n + 1;
+o [ n ] .kind = MOD_BOX ; o [ n ] .dynamic = dyn ;
+double hx = w * 0.5 , hy = h * 0.5 , hz = d * 0.5 ;
+o [ n ] .x = x ;
+o [ n ] .y = y + hy ;
+o [ n ] .z = z ;
+o [ n ] .hx = hx ; o [ n ] .hy = hy ; o [ n ] .hz = hz ;
+o [ n ] .r = 0.6 ; o [ n ] .g = 0.6 ; o [ n ] .b = 0.8 ;
+n = n + 1 ;
 }
 static void text ( double x , double y , double z , char * t ) {
 o [ n ] .kind = MOD_TEXT ;
@@ -24,7 +24,7 @@ int game_build ( ModObject * out , int max ) {
 ( void ) max ;
 o = out ; n = 0 ;
 text ( 0 , - 15 , 5 , "hi" ) ;
-box ( 0 , - 16 , 5 , 2 , 1 , 2, 0 ) ;
-box ( 3 , - 16 , 5 , 2 , 2 , 2, 0 ) ;
+box ( 0 , - 16 , 5 , 2 , 1 , 2 , 0 ) ;
+box ( 3 , - 16 , 5 , 2 , 2 , 2 , 0 ) ;
 return n ;
 }
diff --git a/src/c_token/token.hpp b/src/c_token/token.hpp
index bc86314..6c5deac 100644
--- a/src/c_token/token.hpp
+++ b/src/c_token/token.hpp
@@ -2,7 +2,6 @@
 
 #include <cstdint>
 #include <string>
-#include <vector>
 
 enum class TokenType : uint8_t {
     Keyword,
diff --git a/src/c_token/token_db.cpp b/src/c_token/token_db.cpp
index e7de793..f99a444 100644
--- a/src/c_token/token_db.cpp
+++ b/src/c_token/token_db.cpp
@@ -1,7 +1,7 @@
 #include "token_db.hpp"
 #include <unordered_map>
 
-static const std::vector<Token>
+static const DynArray<Token>
 all_tokens = {
     { "int",    TokenType::Type,       "Integer type",         "Declares a 32-bit integer variable" },
     { "float",  TokenType::Type,       "Float type",           "Declares a floating point variable" },
@@ -59,21 +59,22 @@ build_index()
 }
 
 
-static const auto index = build_index();
+static const std::unordered_map<std::string, const Token*>
+token_index = build_index();
 
 
 const Token*
 TokenDB::find(const std::string& text)
 {
-    auto it = index.find(text);
-    return (it != index.end()) ? it->second : nullptr;
+    auto it = token_index.find(text);
+    return (it != token_index.end()) ? it->second : nullptr;
 }
 
 
-std::vector<const Token*>
+DynArray<const Token*>
 TokenDB::of_type(TokenType type)
 {
-    std::vector<const Token*> result;
+    DynArray<const Token*> result;
     for (const auto& t : all_tokens)
         if (t.type == type)
             result.push_back(&t);
@@ -99,6 +100,6 @@ TokenDB::type_name(TokenType t)
 bool
 TokenDB::known(const std::string& text)
 {
-    return index.find(text) != index.end();
+    return token_index.find(text) != token_index.end();
 }
 
diff --git a/src/c_token/token_db.hpp b/src/c_token/token_db.hpp
index 9668a58..0e7268e 100644
--- a/src/c_token/token_db.hpp
+++ b/src/c_token/token_db.hpp
@@ -1,11 +1,13 @@
 #pragma once
+
 #include "token.hpp"
+#include "utils/globals.hpp"
 
 namespace TokenDB
 {
-    const Token*              find(const std::string& text);
-    std::vector<const Token*> of_type(TokenType type);
-    const char*               type_name(TokenType type);
+    const Token*           find(const std::string& text);
+    DynArray<const Token*> of_type(TokenType type);
+    const char*            type_name(TokenType type);
 
-    bool known(const std::string& text);
+    bool                   known(const std::string& text);
 }
diff --git a/src/entities/player.hpp b/src/entities/player.hpp
index 27fbff5..ddee694 100644
--- a/src/entities/player.hpp
+++ b/src/entities/player.hpp
@@ -1,7 +1,6 @@
 #pragma once
 
 #include <SDL3/SDL.h>
-#include <vector>
 
 #include "systems/input_system.hpp"
 #include "text_3d/text_3d.hpp"
diff --git a/src/entities/pult.hpp b/src/entities/pult.hpp
index a528f67..604e43b 100644
--- a/src/entities/pult.hpp
+++ b/src/entities/pult.hpp
@@ -1,10 +1,10 @@
 #pragma once
 
 #include <string>
-#include <vector>
 
 #include "entities/solid_box.hpp"
 #include "text_3d/text_label.hpp"
+#include "utils/globals.hpp"
 #include "utils/light.hpp"
 #include "utils/math.hpp"
 
@@ -42,7 +42,7 @@ struct PultButton final : public SolidBox
 struct Pult : public SolidBox
 {
     std::string              name;
-    std::vector<PultButton*> buttons;
+    DynArray<PultButton*> buttons;
     TextLabel                label;
 
     static constexpr float W = 0.50f;
diff --git a/src/entities/world.cpp b/src/entities/world.cpp
index eab258f..485ea5c 100644
--- a/src/entities/world.cpp
+++ b/src/entities/world.cpp
@@ -104,7 +104,7 @@ World::draw(GlRenderer& gl, const Camera& camera, const Light& light) const
 
     // Box edges: dark grey
     if (show_wireframe) {
-        std::vector<float> pts;
+        DynArray<float> pts;
         pts.reserve(map_box_edges_len * 6);
         for (size_t k = 0; k < map_box_edges_len; k++) {
             const Edge& e = edges[k];
diff --git a/src/levels/goal.cpp b/src/levels/goal.cpp
index ce35d6d..2c029eb 100644
--- a/src/levels/goal.cpp
+++ b/src/levels/goal.cpp
@@ -7,13 +7,13 @@
 namespace {
 
 bool
-contains(const std::vector<std::string>& v, const std::string& s)
+contains(const DynArray<std::string>& v, const std::string& s)
 {
     return std::find(v.begin(), v.end(), s) != v.end();
 }
 
 bool
-contains(const std::vector<PultAction>& v, PultAction a)
+contains(const DynArray<PultAction>& v, PultAction a)
 {
     return std::find(v.begin(), v.end(), a) != v.end();
 }
diff --git a/src/levels/goal.hpp b/src/levels/goal.hpp
index b212d9c..f35d73e 100644
--- a/src/levels/goal.hpp
+++ b/src/levels/goal.hpp
@@ -2,9 +2,9 @@
 
 #include <string>
 #include <variant>
-#include <vector>
 
 #include "entities/pult.hpp"   // for PultAction
+#include "utils/globals.hpp"
 
 
 // ─────────────────────────────────────────────────────────────────────────
@@ -68,11 +68,11 @@ using Goal = std::variant<
 
 
 // Composition: any one option satisfies the goal.
-struct AnyOf { std::vector<Goal> options; };
+struct AnyOf { DynArray<Goal> options; };
 
 // Composition: all options must hold simultaneously.
 // Empty AllOf{} is intentionally unsatisfiable.
-struct AllOf { std::vector<Goal> options; };
+struct AllOf { DynArray<Goal> options; };
 
 
 // ─────────────────────────────────────────────────────────────────────────
@@ -83,10 +83,10 @@ struct AllOf { std::vector<Goal> options; };
 // reach into GameState directly.
 // ─────────────────────────────────────────────────────────────────────────
 struct GoalContext {
-    std::vector<std::string> active_plate_tags;
-    std::vector<PultAction>  buttons_pressed_this_step;
-    std::vector<std::string> picked_up_this_step;
-    std::vector<std::string> ai_words_defeated_this_step;
+    DynArray<std::string> active_plate_tags;
+    DynArray<PultAction>  buttons_pressed_this_step;
+    DynArray<std::string> picked_up_this_step;
+    DynArray<std::string> ai_words_defeated_this_step;
 
     // Action flags — cleared on step advance.
     bool                     dropped_this_step    {false};
diff --git a/src/levels/level.hpp b/src/levels/level.hpp
index d3ccda5..6e5a821 100644
--- a/src/levels/level.hpp
+++ b/src/levels/level.hpp
@@ -1,14 +1,14 @@
 #pragma once
 
 #include <string>
-#include <vector>
 
 #include "goal.hpp"
 #include "scene.hpp"
+#include "utils/globals.hpp"
 
 
 struct TokenGrant {
-    std::vector<std::string> choices;
+    DynArray<std::string> choices;
 
     static TokenGrant give(const std::string& text) {
         return { { text } };
@@ -22,7 +22,7 @@ struct TokenGrant {
 struct LevelStep {
     std::string              hint;
     std::string              details;
-    std::vector<TokenGrant>  grants;
+    DynArray<TokenGrant>  grants;
     Scene                    scene;
     bool                     reset_carry = false;
     Goal                     goal = AllOf{};
@@ -35,5 +35,5 @@ struct Level {
     std::string desc;
     bool        dynamic_lib = false;
     std::string source_file = {};
-    std::vector<LevelStep> steps;
+    DynArray<LevelStep> steps;
 };
diff --git a/src/levels/level_list.cpp b/src/levels/level_list.cpp
index 7d4b63a..6d5a3e7 100644
--- a/src/levels/level_list.cpp
+++ b/src/levels/level_list.cpp
@@ -1,7 +1,7 @@
 #include "level_list.hpp"
 #include "utils/config.hpp"
 
-static const std::vector<Level> levels =
+static const DynArray<Level> levels =
 {
     {
         .name = "Tutorial",
diff --git a/src/levels/level_list.hpp b/src/levels/level_list.hpp
index d0ee704..cfdc87c 100644
--- a/src/levels/level_list.hpp
+++ b/src/levels/level_list.hpp
@@ -1,7 +1,7 @@
 #pragma once
 
 #include "level.hpp"
-#include <vector>
+#include "utils/globals.hpp"
 
 const Level& get_Level(size_t index);
 size_t       level_Count();
diff --git a/src/levels/level_runner.cpp b/src/levels/level_runner.cpp
index f4c31c4..62b809e 100644
--- a/src/levels/level_runner.cpp
+++ b/src/levels/level_runner.cpp
@@ -8,9 +8,9 @@ bool LevelRunner::advance()
     return !complete();
 }
 
-std::vector<std::string> LevelRunner::grants_for_step() const
+DynArray<std::string> LevelRunner::grants_for_step() const
 {
-    std::vector<std::string> result;
+    DynArray<std::string> result;
     for (const TokenGrant& grant : current().grants)
         for (const std::string& c : grant.choices)
             result.push_back(c);
diff --git a/src/levels/level_runner.hpp b/src/levels/level_runner.hpp
index fcbff14..a141034 100644
--- a/src/levels/level_runner.hpp
+++ b/src/levels/level_runner.hpp
@@ -17,7 +17,7 @@ public:
 
     // All tokens to spawn for the current step (resolved — no choices yet)
     // Choices are returned as multiple Text3d objects placed near each other
-    std::vector<std::string> grants_for_step() const;
+    DynArray<std::string> grants_for_step() const;
 
 private:
     const Level* level;
diff --git a/src/levels/scene.hpp b/src/levels/scene.hpp
index b17f3ba..954f60d 100644
--- a/src/levels/scene.hpp
+++ b/src/levels/scene.hpp
@@ -2,9 +2,9 @@
 
 #include <SDL3/SDL.h>
 #include <string>
-#include <vector>
 
 #include "text_3d/text_3d.hpp"
+#include "utils/globals.hpp"
 #include "utils/math.hpp"
 
 
@@ -50,8 +50,8 @@ struct PlacedAi {
 // All spatial content for a single step. Spawned on step entry, removed
 // on step advance (or reset).
 struct Scene {
-    std::vector<PlacedWord>     words;
-    std::vector<PlacedPlate>    plates;
-    std::vector<PlacedPlatform> platforms;
-    std::vector<PlacedAi>       ai_seeds;
+    DynArray<PlacedWord>     words;
+    DynArray<PlacedPlate>    plates;
+    DynArray<PlacedPlatform> platforms;
+    DynArray<PlacedAi>       ai_seeds;
 };
diff --git a/src/rendering/floor_mandelbrot.hpp b/src/rendering/floor_mandelbrot.hpp
index 9d46dc7..c223ee2 100644
--- a/src/rendering/floor_mandelbrot.hpp
+++ b/src/rendering/floor_mandelbrot.hpp
@@ -1,9 +1,9 @@
 #pragma once
 
 #include <SDL3/SDL.h>
-#include <vector>
 
 #include "rendering/gl_renderer.hpp"
+#include "utils/globals.hpp"
 #include "utils/light.hpp"
 #include "utils/math.hpp"
 
@@ -26,6 +26,6 @@ public:
     void draw(GlRenderer& gl, const Camera& cam, const Light& light) const;
 
 private:
-    std::vector<float> verts_;
-    std::vector<int>   indices_;
+    DynArray<float> verts_;
+    DynArray<int>   indices_;
 };
diff --git a/src/rendering/gl_renderer.cpp b/src/rendering/gl_renderer.cpp
index 06b92fc..50290df 100644
--- a/src/rendering/gl_renderer.cpp
+++ b/src/rendering/gl_renderer.cpp
@@ -258,8 +258,8 @@ void main()
 // Layout: [px,py,pz, nx,ny,nz] per vertex, 4 verts per face, 2 tris per face.
 // ─────────────────────────────────────────────────────────────────────────────
 static void box_Mesh(float hx, float hy, float hz,
-                     std::vector<float>& verts,
-                     std::vector<int>&   indices)
+                     DynArray<float>& verts,
+                     DynArray<int>&   indices)
 {
     struct Face { float nx,ny,nz; float v[4][3]; };
     const Face faces[6] = {
@@ -293,9 +293,9 @@ static void box_Mesh(float hx, float hy, float hz,
 // ─────────────────────────────────────────────────────────────────────────────
 void GlRenderer::draw_Box(float hx, float hy, float hz)
 {
-    std::vector<float> verts;
+    DynArray<float> verts;
     verts.reserve(6 * 4 * 6);
-    std::vector<int>   indices;
+    DynArray<int>   indices;
     indices.reserve(6 * 6);
     box_Mesh(hx, hy, hz, verts, indices);
     draw_Mesh(verts.data(), static_cast<int>(verts.size()) / 6,
diff --git a/src/rendering/gl_renderer.hpp b/src/rendering/gl_renderer.hpp
index 2bdc1f8..e43da87 100644
--- a/src/rendering/gl_renderer.hpp
+++ b/src/rendering/gl_renderer.hpp
@@ -15,9 +15,9 @@
 
 #include <SDL3/SDL.h>
 #include <glad/glad.h>
-#include <vector>
 
 #include "entities/camera.hpp"
+#include "utils/globals.hpp"
 #include "utils/light.hpp"
 #include "utils/math.hpp"
 
diff --git a/src/states/game_context.hpp b/src/states/game_context.hpp
index a21caff..c9402d3 100644
--- a/src/states/game_context.hpp
+++ b/src/states/game_context.hpp
@@ -1,10 +1,10 @@
 #pragma once
 
 #include <memory>
-#include <vector>
 
 #include "entities/game_object.hpp"
 #include "text_3d/text_3d.hpp"
+#include "utils/globals.hpp"
 #include "wall/text_wall.hpp"
 
 class  Player;
@@ -20,9 +20,9 @@ struct GameContext
     cpputils::unique_ptr<World>  world;
     cpputils::unique_ptr<Light>  light;
 
-    std::vector<Text3dPtr>       words;
-    std::vector<GameObjectPtr>   scenery;
-    std::vector<PultButton*>     buttons;
+    DynArray<Text3dPtr>       words;
+    DynArray<GameObjectPtr>   scenery;
+    DynArray<PultButton*>     buttons;
 
     TextWallList                 wall_texts;
 };
diff --git a/src/states/game_state.cpp b/src/states/game_state.cpp
index 47341f4..f60ec97 100644
--- a/src/states/game_state.cpp
+++ b/src/states/game_state.cpp
@@ -54,7 +54,7 @@ GameState::GameState(size_t starting_level)
     if (starting_level >= level_Count()) starting_level = 0;
     level_runner    = cpputils::make_unique<LevelRunner>(get_Level(starting_level));
 
-    std::vector<std::string> tokens = level_runner->grants_for_step();
+    DynArray<std::string> tokens = level_runner->grants_for_step();
     step_Spawn(tokens);
 
     scene_Setup(level_runner->current().scene);
@@ -88,7 +88,7 @@ GameState::GameState(size_t starting_level)
 void
 GameState::debug_SpawnWords(size_t n)
 {
-    std::vector<std::string> tokens;
+    DynArray<std::string> tokens;
     tokens.reserve(n);
     static const char* samples[] = {
         "int", "for", "while", "return", "void", "char",
@@ -121,8 +121,8 @@ GameState::carriedline_Update()
 
     auto [anchor, right, glyph_yaw] = anchorframe_Current();
 
-    std::vector<Vec3>             positions = CarryMath::slot_positions(carried, anchor, right, CarryMath::CARRY_GAP);
-    std::vector<CarriedWordNode*> nodes     = carried.reverse_nodes();
+    DynArray<Vec3>             positions = CarryMath::slot_positions(carried, anchor, right, CarryMath::CARRY_GAP);
+    DynArray<CarriedWordNode*> nodes     = carried.reverse_nodes();
 
     for (size_t i = 0; i < nodes.size() && i < positions.size(); ++i) {
         if (nodes[i]->word) {
@@ -252,7 +252,7 @@ GameState::on_ShootOne()
     freelook_sys->state.active  = false;
     freelook_sys->state.lerping = false;
 
-    std::vector<Text3dPtr> single;
+    DynArray<Text3dPtr> single;
     single.push_back(std::move(word));
     projectiles_sys->launch(projectiles, std::move(single), ctx.camera->fwd);
 
@@ -277,7 +277,7 @@ GameState::on_ShootAll()
     freelook_sys->state.active  = false;
     freelook_sys->state.lerping = false;
 
-    std::vector<Text3dPtr> all_words;
+    DynArray<Text3dPtr> all_words;
     for (CarriedWordNode* node : carried.nodes())
         if (node->word)
             all_words.push_back(std::move(node->word));
@@ -398,7 +398,7 @@ GameState::on_TextCancel(FrameInput& input)
 
 
 void
-GameState::step_Spawn(const std::vector<std::string>& tokens)
+GameState::step_Spawn(const DynArray<std::string>& tokens)
 {
     static constexpr float spacing = 3.0f;
     static constexpr int   per_row = 8;
@@ -465,7 +465,7 @@ GameState::projectile_LandOnWall(ProjectileResult& result)
 
 void
 GameState::walltext_HandleShootDown(ProjectileResult& result,
-                                  std::vector<size_t>& to_erase)
+                                  DynArray<size_t>& to_erase)
 {
     if (result.wall_text_index < ctx.wall_texts.size()) {
         TextWallPtr& wt = ctx.wall_texts[result.wall_text_index];
@@ -588,7 +588,7 @@ GameState::update(double dt)
 
 
     { FP_ZONE("update.particles");
-        std::vector<GameObject*> particle_colliders;
+        DynArray<GameObject*> particle_colliders;
         particle_colliders.reserve(ctx.scenery.size());
         for (const GameObjectPtr& s : ctx.scenery)
             if (s) particle_colliders.push_back(s.get());
@@ -658,7 +658,7 @@ GameState::render(SDL_Window& window, GlRenderer& renderer)
     }
 
     { FP_ZONE("render.carried");
-        std::vector<CarriedWordNode*> nodes = carried.nodes();
+        DynArray<CarriedWordNode*> nodes = carried.nodes();
         for (const CarriedWordNode* node : nodes)
             if (node && node->word)
                 node->word->draw(renderer, *ctx.camera, *ctx.light);
@@ -736,7 +736,7 @@ GameState::infowall_StepdetailsShow()
     Wall::Transform  wall_transform = Wall::get_Transform(Wall::Role::Info, world_scale);
 
     constexpr size_t MAX_INFO_LINE_CHARS = 92;
-    std::vector<std::string> lines = wrap_Text(details, MAX_INFO_LINE_CHARS);
+    DynArray<std::string> lines = wrap_Text(details, MAX_INFO_LINE_CHARS);
 
     const float bottom_y = -world_scale + 1.0f;
     float y = bottom_y + static_cast<float>(lines.size() - 1) * Wall::TEXT_LINE_SPACING;
@@ -844,7 +844,7 @@ GameState::substep(double dt)
                                     dt);
     }
 
-    std::vector<GameObject*> colliders;
+    DynArray<GameObject*> colliders;
     { FP_ZONE("subst.build_colliders");
         colliders.reserve(ctx.scenery.size() + ctx.words.size());
         for (const GameObjectPtr& p : ctx.scenery) if (p) colliders.push_back(p.get());
@@ -856,7 +856,7 @@ GameState::substep(double dt)
     }
 
     { FP_ZONE("subst.word_ai");
-        std::vector<Text3dPtr> ai_dropped;
+        DynArray<Text3dPtr> ai_dropped;
         word_ai_sys->update(ctx.words, *ctx.player, carried, ai_dropped, dt);
         for (Text3dPtr& d : ai_dropped) {
             if (d) ctx.words.push_back(std::move(d));
@@ -867,9 +867,9 @@ GameState::substep(double dt)
         physics_sys->update_Projectiles(projectiles, dt);
     }
 
-    std::vector<ProjectileContactEvent> projectile_contacts;
+    DynArray<ProjectileContactEvent> projectile_contacts;
 
-    std::vector<TextLabel*> all_labels = pult_sys->labels();
+    DynArray<TextLabel*> all_labels = pult_sys->labels();
     { FP_ZONE("subst.hint_and_labels");
         hint_sys->update(dt, *ctx.camera);
         for (TextLabel* lbl : all_labels)
@@ -882,7 +882,7 @@ GameState::substep(double dt)
         physics_sys->resolve_Projectiles(projectiles, &colliders, &all_labels, &projectile_contacts);
     }
 
-    std::vector<WallTextHitTest> wall_text_hits;
+    DynArray<WallTextHitTest> wall_text_hits;
     { FP_ZONE("subst.build_wall_hits");
         wall_text_hits.reserve(ctx.wall_texts.size());
         for (size_t i = 0; i < ctx.wall_texts.size(); ++i) {
@@ -897,13 +897,13 @@ GameState::substep(double dt)
         }
     }
 
-    std::vector<ProjectileResult> proj_results;
+    DynArray<ProjectileResult> proj_results;
     { FP_ZONE("subst.projectile_sys");
         projectiles_sys->update(projectiles, proj_results,
                                 wall_text_hits, dt, ctx.world->scale());
     }
 
-    std::vector<size_t> wall_text_to_erase;
+    DynArray<size_t> wall_text_to_erase;
 
     { FP_ZONE("subst.projectile_land");
         for (ProjectileResult& result : proj_results) {
@@ -1017,7 +1017,7 @@ GameState::level_Advance()
             carried = CarriedLine();
         }
 
-        std::vector<std::string> tokens = level_runner->grants_for_step();
+        DynArray<std::string> tokens = level_runner->grants_for_step();
         step_Spawn(tokens);
         scene_Setup(level_runner->current().scene);
         step_SettingsApply();
@@ -1050,7 +1050,7 @@ GameState::mod_Clear()
 
 
 void
-GameState::mod_Apply(const std::vector<ModObject>& objs)
+GameState::mod_Apply(const DynArray<ModObject>& objs)
 {
     mod_Clear();
 
diff --git a/src/states/game_state.hpp b/src/states/game_state.hpp
index e63f186..11260d8 100644
--- a/src/states/game_state.hpp
+++ b/src/states/game_state.hpp
@@ -1,8 +1,6 @@
 #pragma once
 
 #include <memory>
-#include <vector>
-
 
 #include "cpputils/unique_ptr.hpp"
 #include "entities/plate.hpp"
@@ -27,14 +25,15 @@
 #include "systems/word_ai_system.hpp"
 #include "systems/word_anim.hpp"
 #include "utils/carry_math.hpp"
+#include "utils/globals.hpp"
 #include "wall/editor_wall.hpp"
 #include "wall/text_wall.hpp"
 
 
 struct StepLedger {
-    std::vector<PultAction>  buttons_pressed;
-    std::vector<std::string> ai_defeated;
-    std::vector<std::string> picked_up;
+    DynArray<PultAction>  buttons_pressed;
+    DynArray<std::string> ai_defeated;
+    DynArray<std::string> picked_up;
     bool compile_ok {false};
     bool dropped    {false};
     bool shot       {false};
@@ -72,16 +71,16 @@ class GameState : public IState
     double accumulator    { 0.0 };
     double alpha          { 0.0 };
 
-    std::vector<Text3d*> typing_words;
+    DynArray<Text3d*> typing_words;
     std::string          typing_last;
 
-    std::vector<Projectile>    projectiles;
-    std::vector<WordAnim_Add>  anims_add;
-    std::vector<WordAnim_Drop> anims_drop;
+    DynArray<Projectile>    projectiles;
+    DynArray<WordAnim_Add>  anims_add;
+    DynArray<WordAnim_Drop> anims_drop;
 
-    std::vector<GameObject*> mod_scenery;
-    std::vector<GameObject*> step_scenery;
-    std::vector<StepPlate>   step_plates;
+    DynArray<GameObject*> mod_scenery;
+    DynArray<GameObject*> step_scenery;
+    DynArray<StepPlate>   step_plates;
 
     cpputils::unique_ptr<InputSystem>      input_sys;
     cpputils::unique_ptr<PhysicsSystem>    physics_sys;
@@ -124,14 +123,14 @@ class GameState : public IState
     void carriedline_Update();
     void carry_BeginAnim(Text3dPtr word, bool with_pitch);
     void pult_DispatchAction(PultAction act);
-    void step_Spawn(const std::vector<std::string>& tokens);
+    void step_Spawn(const DynArray<std::string>& tokens);
     void step_SettingsApply();
     void infowall_Clear();
     void infowall_StepdetailsShow();
 
     // dynamic-lib sandbox
     void mod_Clear();
-    void mod_Apply(const std::vector<ModObject>& objs);
+    void mod_Apply(const DynArray<ModObject>& objs);
     void sandbox_LoadFileToWall();
     void sandbox_Restore();
     std::string sandbox_CanonicalPath() const;
@@ -145,7 +144,7 @@ class GameState : public IState
 
     void projectile_LandOnFloor(ProjectileResult& result);
     void projectile_LandOnWall (ProjectileResult& result);
-    void walltext_HandleShootDown(ProjectileResult& result, std::vector<size_t>& to_erase);
+    void walltext_HandleShootDown(ProjectileResult& result, DynArray<size_t>& to_erase);
 
     void debug_SpawnWords(size_t n);
 
diff --git a/src/states/menu_state.hpp b/src/states/menu_state.hpp
index 72f7b3e..c536606 100644
--- a/src/states/menu_state.hpp
+++ b/src/states/menu_state.hpp
@@ -1,13 +1,13 @@
 #pragma once
 
 #include <string>
-#include <vector>
 
 #include "cpputils/unique_ptr.hpp"
 #include "entities/camera.hpp"
 #include "entities/world.hpp"
 #include "istate.hpp"
 #include "text_3d/text_3d.hpp"
+#include "utils/globals.hpp"
 #include "utils/light.hpp"
 
 
@@ -48,7 +48,7 @@ private:
     cpputils::unique_ptr<Light>  light;
 
     Text3dPtr             title;
-    std::vector<MenuItem> items;
+    DynArray<MenuItem> items;
     size_t                selected{0};
 
     // Vertical scroll of the level list.
diff --git a/src/systems/carry_system.cpp b/src/systems/carry_system.cpp
index f2a4101..827bce3 100644
--- a/src/systems/carry_system.cpp
+++ b/src/systems/carry_system.cpp
@@ -70,10 +70,10 @@ CarriedLine::pop_front()
     return word;
 }
 
-std::vector<CarriedWordNode*>
+DynArray<CarriedWordNode*>
 CarriedLine::nodes() const
 {
-    std::vector<CarriedWordNode*> result;
+    DynArray<CarriedWordNode*> result;
     for (CarriedWordNode* node = first.get(); node; node = node->next.get()) {
         result.push_back(node);
     }
@@ -81,10 +81,10 @@ CarriedLine::nodes() const
 }
 
 
-std::vector<CarriedWordNode*> 
+DynArray<CarriedWordNode*> 
 CarriedLine::reverse_nodes() const
 {
-    std::vector<CarriedWordNode*> result;
+    DynArray<CarriedWordNode*> result;
     for (CarriedWordNode* node = last; node; node = node->prev) {
         result.push_back(node);
     }
@@ -103,7 +103,7 @@ void
 CarriedLine::compact()
 {
     // Walk and unlink empties. Easiest: rebuild from non-null words.
-    std::vector<Text3dPtr> survivors;
+    DynArray<Text3dPtr> survivors;
     for (CarriedWordNode* node = first.get(); node; node = node->next.get())
         if (node->word) survivors.push_back(std::move(node->word));
 
diff --git a/src/systems/carry_system.hpp b/src/systems/carry_system.hpp
index ab9458e..2bfd76d 100644
--- a/src/systems/carry_system.hpp
+++ b/src/systems/carry_system.hpp
@@ -1,8 +1,8 @@
 #pragma once
 #include <memory>
-#include <vector>
-#include "utils/globals.hpp"
+
 #include "text_3d/text_3d.hpp"
+#include "utils/globals.hpp"
 
 
 
@@ -24,9 +24,9 @@ struct CarriedLine {
     Text3dPtr pop_back();
     Text3dPtr pop_front();
 
-    std::vector<CarriedWordNode*> nodes() const;
+    DynArray<CarriedWordNode*> nodes() const;
 
-    std::vector<CarriedWordNode*> reverse_nodes() const;
+    DynArray<CarriedWordNode*> reverse_nodes() const;
 
     void clear();
 
diff --git a/src/systems/compiler_system.hpp b/src/systems/compiler_system.hpp
index ae05f9f..1ddd14f 100644
--- a/src/systems/compiler_system.hpp
+++ b/src/systems/compiler_system.hpp
@@ -1,7 +1,8 @@
 #pragma once
 #include <string>
-#include <vector>
 #include <cstdio>
+
+#include "utils/globals.hpp"
 #include "wall/text_wall.hpp"
 
 struct CompilerSystem
diff --git a/src/systems/input_system.hpp b/src/systems/input_system.hpp
index 9532c80..87a7823 100644
--- a/src/systems/input_system.hpp
+++ b/src/systems/input_system.hpp
@@ -2,7 +2,8 @@
 
 #include <SDL3/SDL.h>
 #include <string>
-#include <vector>
+
+#include "utils/globals.hpp"
 
 class Game;
 
@@ -36,7 +37,7 @@ struct FrameInput {
     bool        text_commit = false;     // Enter pressed this frame
     bool        text_cancel = false;     // Esc pressed this frame
 
-    std::vector<InputCommand> commands;
+    DynArray<InputCommand> commands;
 };
 
 class InputSystem {
diff --git a/src/systems/mod_loader.cpp b/src/systems/mod_loader.cpp
index 9adcd93..0bb5008 100644
--- a/src/systems/mod_loader.cpp
+++ b/src/systems/mod_loader.cpp
@@ -165,10 +165,10 @@ ModLoader::push_Line(TextWallList& texts, const std::string& line, float world_s
     count_++;
 }
 
-std::vector<ModObject>
+DynArray<ModObject>
 ModLoader::load_Objects()
 {
-    std::vector<ModObject> result;
+    DynArray<ModObject> result;
     if (last_so_path_.empty()) return result;
 
     // Close the previous handle before loading the new one. Safe because the
diff --git a/src/systems/mod_loader.hpp b/src/systems/mod_loader.hpp
index 23aaf2c..b1f2925 100644
--- a/src/systems/mod_loader.hpp
+++ b/src/systems/mod_loader.hpp
@@ -2,9 +2,9 @@
 
 #include <cstdio>
 #include <string>
-#include <vector>
 
 #include "systems/game_mod_abi.h"
+#include "utils/globals.hpp"
 #include "wall/text_wall.hpp"
 
 class ModLoader
@@ -18,7 +18,7 @@ public:
 
     // Checkpoint 2: dlopen the last successful .so, call game_build,
     // return the spawned descriptions. Empty vector on any failure.
-    std::vector<ModObject> load_Objects();
+    DynArray<ModObject> load_Objects();
 
     ~ModLoader();   // dlclose on teardown
 
diff --git a/src/systems/particle_system.cpp b/src/systems/particle_system.cpp
index 6bddce8..c2fdd83 100644
--- a/src/systems/particle_system.cpp
+++ b/src/systems/particle_system.cpp
@@ -63,7 +63,7 @@ ParticleSystem::emit_FireworkBurst(const Vec3& center,
 
 void
 ParticleSystem::update(double dt,
-                       const std::vector<GameObject*>& solid_colliders,
+                       const DynArray<GameObject*>& solid_colliders,
                        float world_scale)
 {
     const double ws = static_cast<double>(world_scale);
diff --git a/src/systems/particle_system.hpp b/src/systems/particle_system.hpp
index bf07ecd..13127ec 100644
--- a/src/systems/particle_system.hpp
+++ b/src/systems/particle_system.hpp
@@ -1,9 +1,9 @@
 #pragma once
 
 #include <SDL3/SDL.h>
-#include <vector>
 
 #include "rendering/gl_renderer.hpp"
+#include "utils/globals.hpp"
 #include "utils/light.hpp"
 #include "utils/math.hpp"
 
@@ -40,7 +40,7 @@ class ParticleSystem
 {
 public:
     void update(double dt,
-                const std::vector<GameObject*>& solid_colliders,
+                const DynArray<GameObject*>& solid_colliders,
                 float world_scale);
 
     void draw(GlRenderer& gl, const Camera& cam, const Light& light) const;
@@ -55,5 +55,5 @@ public:
     bool empty() const { return particles_.empty(); }
 
 private:
-    std::vector<Particle> particles_;
+    DynArray<Particle> particles_;
 };
diff --git a/src/systems/physics_system.cpp b/src/systems/physics_system.cpp
index 7159647..3e4de5b 100644
--- a/src/systems/physics_system.cpp
+++ b/src/systems/physics_system.cpp
@@ -43,7 +43,7 @@ PhysicsSystem::update_Player(Player& p, double acc_x, double acc_z, double dt)
 
 void
 PhysicsSystem::resolve_PlayerVsObjects(Player& player, CarriedLine& carried,
-                                       const std::vector<GameObject*>& colliders)
+                                       const DynArray<GameObject*>& colliders)
 {
     const double px_min = player.pos.x - static_cast<double>(player.radius);
     const double px_max = player.pos.x + static_cast<double>(player.radius);
@@ -52,7 +52,7 @@ PhysicsSystem::resolve_PlayerVsObjects(Player& player, CarriedLine& carried,
     const double pz_min = player.pos.z - static_cast<double>(player.radius);
     const double pz_max = player.pos.z + static_cast<double>(player.radius);
 
-    std::vector<CarriedWordNode*> nodes = carried.nodes();
+    DynArray<CarriedWordNode*> nodes = carried.nodes();
 
     for (GameObject* obj : colliders) {
         if (!obj || !obj->solid) continue;
@@ -190,7 +190,7 @@ PhysicsSystem::try_Pair(GameObject* a, GameObject* b)
 /// Objects
 
 void
-PhysicsSystem::update_Objects(const std::vector<GameObject*>& objects,
+PhysicsSystem::update_Objects(const DynArray<GameObject*>& objects,
                               double dt, double gravity, float world_scale)
 {
     // Integration pass
@@ -235,7 +235,7 @@ PhysicsSystem::update_Objects(const std::vector<GameObject*>& objects,
         grid_.build(objects, static_cast<double>(world_scale));
 
         // Only active bodies initiate collision tests.
-        std::vector<GameObject*> active;
+        DynArray<GameObject*> active;
         active.reserve(objects.size());
         for (GameObject* o : objects) {
             if (!o) continue;
@@ -282,7 +282,7 @@ PhysicsSystem::resolve_WorldBounds(GameObject& obj, float world_scale)
 /// Projectile
 
 void
-PhysicsSystem::update_Projectiles(std::vector<Projectile>& projectiles, double dt)
+PhysicsSystem::update_Projectiles(DynArray<Projectile>& projectiles, double dt)
 {
     for (Projectile& p : projectiles) {
         if (p.sticking)      continue;
@@ -368,10 +368,10 @@ PhysicsSystem::resolve_SingleBoxCollision(Projectile& proj,
 
 
 void
-PhysicsSystem::resolve_Projectiles(std::vector<Projectile>& projectiles,
-                                   const std::vector<GameObject*>* statics,
-                                   const std::vector<TextLabel*>*  labels,
-                                   std::vector<ProjectileContactEvent>* contacts)
+PhysicsSystem::resolve_Projectiles(DynArray<Projectile>& projectiles,
+                                   const DynArray<GameObject*>* statics,
+                                   const DynArray<TextLabel*>*  labels,
+                                   DynArray<ProjectileContactEvent>* contacts)
 {
     for (Projectile& proj : projectiles) {
         if (proj.sticking || proj.words.empty()) continue;
@@ -461,7 +461,7 @@ PhysicsSystem::resolve_Projectiles(std::vector<Projectile>& projectiles,
 
 
 void
-PhysicsSystem::SpatialGrid::build(const std::vector<GameObject*>& objs, double world_scale)
+PhysicsSystem::SpatialGrid::build(const DynArray<GameObject*>& objs, double world_scale)
 {
     // Cell size ~ a couple world units; tune if objects are much larger/smaller.
     cell = 2.0;
@@ -485,7 +485,7 @@ PhysicsSystem::SpatialGrid::build(const std::vector<GameObject*>& objs, double w
 }
 
 void
-PhysicsSystem::collide_Grid(const std::vector<GameObject*>& active)
+PhysicsSystem::collide_Grid(const DynArray<GameObject*>& active)
 {
     // Each active object tests against objects in its own + 8 neighbour cells.
     for (GameObject* a : active) {
diff --git a/src/systems/physics_system.hpp b/src/systems/physics_system.hpp
index 0694ed4..0bc1a22 100644
--- a/src/systems/physics_system.hpp
+++ b/src/systems/physics_system.hpp
@@ -1,11 +1,10 @@
 #pragma once
 
-#include <vector>
-
-#include "entities/player.hpp"
 #include "entities/game_object.hpp"
+#include "entities/player.hpp"
 #include "systems/carry_system.hpp"
 #include "systems/collision.hpp"
+#include "utils/globals.hpp"
 
 struct Projectile;
 class  TextLabel;
@@ -31,25 +30,25 @@ class PhysicsSystem {
         double inv  = 0.5;
         int    nx = 0, nz = 0;
         double min_x = 0, min_z = 0;
-        std::vector<std::vector<GameObject*>> cells;
+        DynArray<DynArray<GameObject*>> cells;
 
-        void build(const std::vector<GameObject*>& objs, double world_scale);
+        void build(const DynArray<GameObject*>& objs, double world_scale);
         int  index(int cx, int cz) const { return cz * nx + cx; }
     };
     SpatialGrid grid_;
-    void collide_Grid(const std::vector<GameObject*>& active);
+    void collide_Grid(const DynArray<GameObject*>& active);
 
 public:
     void update_Player(Player& player, double acc_x, double acc_z, double dt);
-    void update_Objects(const std::vector<GameObject*>& objects,
+    void update_Objects(const DynArray<GameObject*>& objects,
                         double dt, double gravity, float world_scale);
-    void update_Projectiles(std::vector<Projectile>& projectiles, double dt);
+    void update_Projectiles(DynArray<Projectile>& projectiles, double dt);
 
-    void resolve_Projectiles(std::vector<Projectile>& projectiles,
-                             const std::vector<GameObject*>* statics,
-                             const std::vector<TextLabel*>* labels,
-                             std::vector<ProjectileContactEvent>* contacts);
+    void resolve_Projectiles(DynArray<Projectile>& projectiles,
+                             const DynArray<GameObject*>* statics,
+                             const DynArray<TextLabel*>* labels,
+                             DynArray<ProjectileContactEvent>* contacts);
     void resolve_PlayerVsObjects(Player& player,
                                  CarriedLine& carried,
-                                 const std::vector<GameObject*>& colliders);
+                                 const DynArray<GameObject*>& colliders);
 };
diff --git a/src/systems/pick_system.cpp b/src/systems/pick_system.cpp
index 051da6b..ced445f 100644
--- a/src/systems/pick_system.cpp
+++ b/src/systems/pick_system.cpp
@@ -67,9 +67,9 @@ ang_score(const GameObject& obj, const Vec3& eye, const Vec3& fwd)
 // etc. based on the PickResult.
 PickResult
 pick(const Camera& camera,
-     const std::vector<Text3dPtr>&     words,
-     const std::vector<GameObjectPtr>& scenery,
-     const std::vector<TextWallPtr>&   wall_texts)
+     const DynArray<Text3dPtr>&     words,
+     const DynArray<GameObjectPtr>& scenery,
+     const DynArray<TextWallPtr>&   wall_texts)
 {
     const Vec3& eye = camera.pos;
 
diff --git a/src/systems/pick_system.hpp b/src/systems/pick_system.hpp
index 4749191..e086fd5 100644
--- a/src/systems/pick_system.hpp
+++ b/src/systems/pick_system.hpp
@@ -1,10 +1,11 @@
 #pragma once
 
 #include <cstddef>
-#include <vector>
-#include "entities/game_object.hpp"
+
 #include "entities/camera.hpp"
+#include "entities/game_object.hpp"
 #include "text_3d/text_3d.hpp"
+#include "utils/globals.hpp"
 #include "wall/text_wall.hpp"
 
 enum class PickKind : uint8_t {
@@ -26,7 +27,7 @@ struct PickResult {
 namespace PickSystem
 {
     PickResult pick(const Camera& camera,
-                    const std::vector<Text3dPtr>&     words,
-                    const std::vector<GameObjectPtr>& scenery,
-                    const std::vector<TextWallPtr>&   wall_texts);
+                    const DynArray<Text3dPtr>&     words,
+                    const DynArray<GameObjectPtr>& scenery,
+                    const DynArray<TextWallPtr>&   wall_texts);
 }
diff --git a/src/systems/plate_system.cpp b/src/systems/plate_system.cpp
index f97a345..c2eb291 100644
--- a/src/systems/plate_system.cpp
+++ b/src/systems/plate_system.cpp
@@ -5,7 +5,7 @@
 void
 PlateSystem::update(double dt,
                     const Player& player,
-                    const std::vector<StepPlate>& plates)
+                    const DynArray<StepPlate>& plates)
 {
     const double px_min = player.pos.x - static_cast<double>(player.radius);
     const double px_max = player.pos.x + static_cast<double>(player.radius);
@@ -36,7 +36,7 @@ PlateSystem::update(double dt,
 }
 
 bool
-PlateSystem::any_Active(const std::vector<StepPlate>& plates) const
+PlateSystem::any_Active(const DynArray<StepPlate>& plates) const
 {
     for (const StepPlate& sp : plates)
         if (sp.plate && sp.plate->active) return true;
diff --git a/src/systems/plate_system.hpp b/src/systems/plate_system.hpp
index ce5641e..26fd991 100644
--- a/src/systems/plate_system.hpp
+++ b/src/systems/plate_system.hpp
@@ -1,7 +1,5 @@
 #pragma once
 
-#include <vector>
-
 #include "entities/plate.hpp"
 #include "utils/globals.hpp"
 
@@ -15,8 +13,8 @@ public:
     // is standing on each one. Sets plate->active based on player AABB overlap.
     void update(double dt,
                 const Player& player,
-                const std::vector<StepPlate>& plates);
+                const DynArray<StepPlate>& plates);
 
     // True if the player is currently standing on any plate.
-    bool any_Active(const std::vector<StepPlate>& plates) const;
+    bool any_Active(const DynArray<StepPlate>& plates) const;
 };
diff --git a/src/systems/projectile_system.cpp b/src/systems/projectile_system.cpp
index 5aa4b3a..bb669cd 100644
--- a/src/systems/projectile_system.cpp
+++ b/src/systems/projectile_system.cpp
@@ -61,7 +61,7 @@ wall_reconstruct(Wall::Position p, double plane, double tangent, double y)
 
 // ── Helper: emit a Floor landing result and mark the projectile for removal ──
 static void
-emit_FloorLanding(Projectile& p, std::vector<ProjectileResult>& results)
+emit_FloorLanding(Projectile& p, DynArray<ProjectileResult>& results)
 {
     ProjectileResult r;
     r.landing = ProjectileLanding::Floor;
@@ -78,7 +78,7 @@ emit_FloorLanding(Projectile& p, std::vector<ProjectileResult>& results)
 // TextWall. Returns nothing — completion is detected via emptied p.words.
 static void
 tick_Sticking(Projectile& p, double dt, double ws,
-              std::vector<ProjectileResult>& results)
+              DynArray<ProjectileResult>& results)
 {
     if (!p.sticking || p.words.empty()) return;
 
@@ -119,7 +119,7 @@ tick_Sticking(Projectile& p, double dt, double ws,
 // Returns true if the projectile should be removed (floor landing emitted).
 static bool
 check_FloorCeiling(Projectile& p, const Vec3& centroid, double ws,
-                   std::vector<ProjectileResult>& results)
+                   DynArray<ProjectileResult>& results)
 {
     double word_he_y = 0.07;
     for (const Text3dPtr& w : p.words) {
@@ -175,7 +175,7 @@ detect_WallHit(const Projectile& p, const Vec3& centroid, double dt, double ws,
 static bool
 check_WallTextIntercept(const Projectile& p, const Vec3& centroid, double dt,
                         Wall::Position hit_pos,
-                        const std::vector<WallTextHitTest>& wall_text_hits,
+                        const DynArray<WallTextHitTest>& wall_text_hits,
                         size_t& out_idx)
 {
     const Vec3 next_centroid {
@@ -278,7 +278,7 @@ begin_EditorStick(Projectile& p, Wall::Position hit_pos)
 // axis → falls to the floor instead of bouncing again).
 static bool
 bounce_OffWall(Projectile& p, Wall::Position hit_pos, double ws,
-               std::vector<ProjectileResult>& results)
+               DynArray<ProjectileResult>& results)
 {
     const bool x_axis = (hit_pos == Wall::Position::PosX ||
                          hit_pos == Wall::Position::NegX);
@@ -313,8 +313,8 @@ bounce_OffWall(Projectile& p, Wall::Position hit_pos, double ws,
 static bool
 handle_WallHit(Projectile& p, const Vec3& centroid, Wall::Position hit_pos,
                double dt,double ws,
-               const std::vector<WallTextHitTest>& wall_text_hits,
-               std::vector<ProjectileResult>& results)
+               const DynArray<WallTextHitTest>& wall_text_hits,
+               DynArray<ProjectileResult>& results)
 {
     // Wall-text intercept takes priority: if the path crosses a wall-text,
     // emit a WallText result and drop the projectile.
@@ -340,8 +340,8 @@ handle_WallHit(Projectile& p, const Vec3& centroid, Wall::Position hit_pos,
 
 
 void
-ProjectileSystem::launch(std::vector<Projectile>&   out,
-                         std::vector<Text3dPtr> words,
+ProjectileSystem::launch(DynArray<Projectile>&   out,
+                         DynArray<Text3dPtr> words,
                          const Vec3& fwd)
 {
     if (words.empty()) return;
@@ -386,9 +386,9 @@ ProjectileSystem::launch(std::vector<Projectile>&   out,
 
 
 void
-ProjectileSystem::update(std::vector<Projectile>&        projectiles,
-                         std::vector<ProjectileResult>&  results,
-                         const std::vector<WallTextHitTest>& wall_text_hits,
+ProjectileSystem::update(DynArray<Projectile>&        projectiles,
+                         DynArray<ProjectileResult>&  results,
+                         const DynArray<WallTextHitTest>& wall_text_hits,
                          double dt, float world_scale)
 {
     const double ws = static_cast<double>(world_scale);
diff --git a/src/systems/projectile_system.hpp b/src/systems/projectile_system.hpp
index d786bcc..ed4e33a 100644
--- a/src/systems/projectile_system.hpp
+++ b/src/systems/projectile_system.hpp
@@ -1,9 +1,9 @@
 #pragma once
 
 #include <cstdint>
-#include <vector>
 
 #include "text_3d/text_3d.hpp"
+#include "utils/globals.hpp"
 #include "utils/math.hpp"
 #include "wall/wall.hpp"
 
@@ -11,7 +11,7 @@ static constexpr uint8_t BOUNCED_X = 1 << 0;
 static constexpr uint8_t BOUNCED_Z = 1 << 1;
 
 struct Projectile {
-    std::vector<Text3dPtr> words;
+    DynArray<Text3dPtr> words;
     Vec3     vel{};
     Vec3     dir{};
     Vec3     prev_centroid{};
@@ -48,19 +48,19 @@ struct ProjectileResult {
     ProjectileLanding      landing  = ProjectileLanding::None;
     Wall::Position         wall_pos = Wall::Position::NegZ;
     size_t                 wall_text_index = 0;
-    std::vector<Text3dPtr> words;
+    DynArray<Text3dPtr> words;
     Vec3                   vel{};
 };
 
 
 class ProjectileSystem {
 public:
-    void launch(std::vector<Projectile>& out,
-                std::vector<Text3dPtr> words,
+    void launch(DynArray<Projectile>& out,
+                DynArray<Text3dPtr> words,
                 const Vec3& fwd);
 
-    void update(std::vector<Projectile>&        projectiles,
-                std::vector<ProjectileResult>&  results,
-                const std::vector<WallTextHitTest>& wall_text_hits,
+    void update(DynArray<Projectile>&        projectiles,
+                DynArray<ProjectileResult>&  results,
+                const DynArray<WallTextHitTest>& wall_text_hits,
                 double dt, float world_scale);
 };
diff --git a/src/systems/pult_system.cpp b/src/systems/pult_system.cpp
index d53f59f..83ff7da 100644
--- a/src/systems/pult_system.cpp
+++ b/src/systems/pult_system.cpp
@@ -7,8 +7,8 @@
 
 
 void
-PultSystem::init(std::vector<GameObjectPtr>& scenery,
-                 std::vector<PultButton*>&   buttons_out,
+PultSystem::init(DynArray<GameObjectPtr>& scenery,
+                 DynArray<PultButton*>&   buttons_out,
                  float                       world_scale)
 {
     const float  floor_y = -world_scale;
@@ -72,7 +72,7 @@ PultSystem::init(std::vector<GameObjectPtr>& scenery,
 
 std::optional<PultAction>
 PultSystem::try_Activate(GameObject*                     obj,
-                         const std::vector<PultButton*>& buttons)
+                         const DynArray<PultButton*>& buttons)
 {
     if (!obj) return std::nullopt;
     for (PultButton* btn : buttons) {
@@ -87,7 +87,7 @@ PultSystem::try_Activate(GameObject*                     obj,
 
 std::optional<PultAction>
 PultSystem::handle_ProjectileContact(const ProjectileContactEvent&    ev,
-                                     const std::vector<PultButton*>&  buttons)
+                                     const DynArray<PultButton*>&  buttons)
 {
     if (!ev.other) return std::nullopt;
     for (PultButton* btn : buttons) {
@@ -118,10 +118,10 @@ PultSystem::draw_Labels(GlRenderer& gl, const Camera& camera, const Light& light
 }
 
 
-std::vector<TextLabel*>
+DynArray<TextLabel*>
 PultSystem::labels()
 {
-    std::vector<TextLabel*> out;
+    DynArray<TextLabel*> out;
     out.reserve(pults_.size());
     for (Pult* p : pults_) out.push_back(&p->label);
     return out;
diff --git a/src/systems/pult_system.hpp b/src/systems/pult_system.hpp
index 2ba77ff..2abdb47 100644
--- a/src/systems/pult_system.hpp
+++ b/src/systems/pult_system.hpp
@@ -1,10 +1,10 @@
 #pragma once
 
-#include <vector>
 #include <optional>
-#include "entities/pult.hpp"
-#include "entities/game_object.hpp"
+
 #include "entities/camera.hpp"
+#include "entities/game_object.hpp"
+#include "entities/pult.hpp"
 #include "utils/globals.hpp"
 
 class GlRenderer;
@@ -15,20 +15,20 @@ struct ProjectileContactEvent;
 class PultSystem
 {
 public:
-    void init(std::vector<GameObjectPtr>& scenery,
-              std::vector<PultButton*>&   buttons_out,
+    void init(DynArray<GameObjectPtr>& scenery,
+              DynArray<PultButton*>&   buttons_out,
               float                       world_scale);
     std::optional<PultAction>
          try_Activate(GameObject*                     obj,
-                      const std::vector<PultButton*>& buttons);
+                      const DynArray<PultButton*>& buttons);
     std::optional<PultAction>
          handle_ProjectileContact(const ProjectileContactEvent&    ev,
-                                  const std::vector<PultButton*>&  buttons);
+                                  const DynArray<PultButton*>&  buttons);
     void update(double dt, const Camera& camera);
     void draw_Labels(GlRenderer& gl, const Camera& camera, const Light& light) const;
 
-    std::vector<TextLabel*> labels();
+    DynArray<TextLabel*> labels();
 
 private:
-    std::vector<Pult*> pults_;
+    DynArray<Pult*> pults_;
 };
diff --git a/src/systems/source_tokens.hpp b/src/systems/source_tokens.hpp
index 9ff6b7b..a3642ea 100644
--- a/src/systems/source_tokens.hpp
+++ b/src/systems/source_tokens.hpp
@@ -1,9 +1,10 @@
 #pragma once
-#include <string>
-#include <vector>
 #include <cctype>
 #include <fstream>
 #include <sstream>
+#include <string>
+
+#include "utils/globals.hpp"
 
 // ─────────────────────────────────────────────────────────────────────────
 // SourceTokens — Option 1 tokenizer + file IO for the dynamic-lib sandbox.
@@ -21,10 +22,10 @@
 namespace SourceTokens
 {
 
-inline std::vector<std::string>
+inline DynArray<std::string>
 tokenize(const std::string& src)
 {
-    std::vector<std::string> out;
+    DynArray<std::string> out;
     const size_t n = src.size();
     size_t i = 0;
     while (i < n) {
@@ -100,14 +101,14 @@ tokenize(const std::string& src)
 // Tokenize per source LINE, preserving line breaks. Returns one vector of
 // tokens per non-empty line of the file, so the wall layout can mirror the
 // file's line structure (one editor row per source line).
-inline std::vector<std::vector<std::string>>
+inline DynArray<DynArray<std::string>>
 tokenize_Lines(const std::string& src)
 {
-    std::vector<std::vector<std::string>> lines;
+    DynArray<DynArray<std::string>> lines;
     std::istringstream in(src);
     std::string line;
     while (std::getline(in, line)) {
-        std::vector<std::string> toks = tokenize(line);
+        DynArray<std::string> toks = tokenize(line);
         if (!toks.empty())
             lines.push_back(std::move(toks));
     }
diff --git a/src/systems/word_ai_system.cpp b/src/systems/word_ai_system.cpp
index 1b20f43..20826a3 100644
--- a/src/systems/word_ai_system.cpp
+++ b/src/systems/word_ai_system.cpp
@@ -26,13 +26,13 @@ perpendicular_horizontal(const Vec3& v)
 
 
 void
-WordAiSystem::update(std::vector<Text3dPtr>& words,
+WordAiSystem::update(DynArray<Text3dPtr>& words,
                      const Player& player,
                      CarriedLine& carried,
-                     std::vector<Text3dPtr>& dropped,
+                     DynArray<Text3dPtr>& dropped,
                      double dt)
 {
-    std::vector<Text3dPtr> released;
+    DynArray<Text3dPtr> released;
 
     // Count AI-active words (Attack/Flee/Stunned all count as "active").
     int active_ai_count = 0;
@@ -108,9 +108,9 @@ WordAiSystem::update_Idle(Text3d& w, const Player& p, int active_ai_count, doubl
 
 
 void WordAiSystem::update_Attack(Text3d& w, const Player& p, CarriedLine& carried,
-                                 std::vector<Text3dPtr>& all_words,
-                                 std::vector<Text3dPtr>& dropped,
-                                 std::vector<Text3dPtr>& released,
+                                 DynArray<Text3dPtr>& all_words,
+                                 DynArray<Text3dPtr>& dropped,
+                                 DynArray<Text3dPtr>& released,
                                  double dt)
 {
     const bool has_carry = (!carried.empty() && carried.last && carried.last->word);
@@ -165,7 +165,7 @@ void WordAiSystem::update_Attack(Text3d& w, const Player& p, CarriedLine& carrie
         if (w.ai_has_stolen) {
             // Already stole this session — touching carry does nothing.
         } else {
-            std::vector<CarriedWordNode*> nodes = carried.reverse_nodes();
+            DynArray<CarriedWordNode*> nodes = carried.reverse_nodes();
             for (CarriedWordNode* node : nodes) {
                 if (!node || !node->word) continue;
                 if (!Collision::aabb_Overlap(w, *node->word)) continue;
@@ -223,7 +223,7 @@ void WordAiSystem::update_Attack(Text3d& w, const Player& p, CarriedLine& carrie
 
 
 void
-WordAiSystem::update_Flee(Text3d& w, const Player& p, std::vector<Text3dPtr>& released, double dt)
+WordAiSystem::update_Flee(Text3d& w, const Player& p, DynArray<Text3dPtr>& released, double dt)
 {
     Vec3 away = w.pos - p.pos;
     double dist = length(away);
@@ -272,7 +272,7 @@ WordAiSystem::update_Flee(Text3d& w, const Player& p, std::vector<Text3dPtr>& re
 
 
 void
-WordAiSystem::update_Stunned(Text3d& w, std::vector<Text3dPtr>& released, double dt)
+WordAiSystem::update_Stunned(Text3d& w, DynArray<Text3dPtr>& released, double dt)
 {
     apply_Locomotion(w, Vec3{}, dt);
 
@@ -283,7 +283,7 @@ WordAiSystem::update_Stunned(Text3d& w, std::vector<Text3dPtr>& released, double
 }
 
 void
-WordAiSystem::release_StolenWords(Text3d& w, std::vector<Text3dPtr>& out)
+WordAiSystem::release_StolenWords(Text3d& w, DynArray<Text3dPtr>& out)
 {
     for (Text3dPtr& s : w.ai_stolen) {
         if (!s) continue;
diff --git a/src/systems/word_ai_system.hpp b/src/systems/word_ai_system.hpp
index e3cd51c..d01349b 100644
--- a/src/systems/word_ai_system.hpp
+++ b/src/systems/word_ai_system.hpp
@@ -1,7 +1,7 @@
 #pragma once
 
-#include <vector>
 #include "text_3d/text_3d.hpp"
+#include "utils/globals.hpp"
 
 struct Player;
 struct CarriedLine;
@@ -9,16 +9,16 @@ struct CarriedLine;
 class WordAiSystem {
     void update_Idle    (Text3d& w, const Player& p, int active_ai_count, double dt);
     void update_Attack  (Text3d& w, const Player& p, CarriedLine& carried,
-                         std::vector<Text3dPtr>& all_words,
-                         std::vector<Text3dPtr>& dropped,
-                         std::vector<Text3dPtr>& released,
+                         DynArray<Text3dPtr>& all_words,
+                         DynArray<Text3dPtr>& dropped,
+                         DynArray<Text3dPtr>& released,
                          double dt);
     void update_Flee    (Text3d& w, const Player& p,
-                         std::vector<Text3dPtr>& released, double dt);
-    void update_Stunned (Text3d& w, std::vector<Text3dPtr>& released, double dt);
+                         DynArray<Text3dPtr>& released, double dt);
+    void update_Stunned (Text3d& w, DynArray<Text3dPtr>& released, double dt);
     void update_StolenTrail(Text3d& w, double dt);
 
-    void release_StolenWords(Text3d& w, std::vector<Text3dPtr>& out);
+    void release_StolenWords(Text3d& w, DynArray<Text3dPtr>& out);
 
     void enter_Attack   (Text3d& w);
     void enter_Flee     (Text3d& w);
@@ -42,9 +42,9 @@ class WordAiSystem {
 public:
     void set_MaxActiveAi(int n) { max_active_ai = n; }
     void set_WorldScale(float ws) { world_scale = ws; }
-    void update(std::vector<Text3dPtr>& words,
+    void update(DynArray<Text3dPtr>& words,
                 const Player& player,
                 CarriedLine& carried,
-                std::vector<Text3dPtr>& dropped,
+                DynArray<Text3dPtr>& dropped,
                 double dt);
 };
diff --git a/src/systems/word_anim.cpp b/src/systems/word_anim.cpp
index f3ca6d5..4273741 100644
--- a/src/systems/word_anim.cpp
+++ b/src/systems/word_anim.cpp
@@ -14,10 +14,10 @@ static double smoothstep(double t)
 static double lerp_d(double a, double b, double t) { return a + (b - a) * t; }
 
 void
-WordAnimSystem::update(std::vector<WordAnim_Add>&    anims_add,
-                       std::vector<WordAnim_Drop>&   anims_drop,
+WordAnimSystem::update(DynArray<WordAnim_Add>&    anims_add,
+                       DynArray<WordAnim_Drop>&   anims_drop,
                        CarriedLine&                  carried,
-                       std::vector<Text3dPtr>&       words,
+                       DynArray<Text3dPtr>&       words,
                        double                        dt,
                        double                        floor_y)
 {
diff --git a/src/systems/word_anim.hpp b/src/systems/word_anim.hpp
index 509adea..a867546 100644
--- a/src/systems/word_anim.hpp
+++ b/src/systems/word_anim.hpp
@@ -1,7 +1,8 @@
 #pragma once
-#include <vector>
-#include "text_3d/text_3d.hpp"
+
 #include "systems/carry_system.hpp"
+#include "text_3d/text_3d.hpp"
+#include "utils/globals.hpp"
 
 struct WordAnim_Add {
     Text3dPtr word;
@@ -22,9 +23,9 @@ struct WordAnim_Drop {
 
 class WordAnimSystem {
 public:
-    void update(std::vector<WordAnim_Add>&  anims_add,
-            std::vector<WordAnim_Drop>& anims_drop,
+    void update(DynArray<WordAnim_Add>&  anims_add,
+            DynArray<WordAnim_Drop>& anims_drop,
             CarriedLine&                carried,
-            std::vector<Text3dPtr>&     words,
+            DynArray<Text3dPtr>&     words,
             double dt, double floor_y);
 };
diff --git a/src/text_3d/text_3d.cpp b/src/text_3d/text_3d.cpp
index 75abb94..1a1ef78 100644
--- a/src/text_3d/text_3d.cpp
+++ b/src/text_3d/text_3d.cpp
@@ -1,7 +1,6 @@
 #include <SDL3/SDL.h>
 #include <algorithm>
 #include <cstdlib>
-#include <vector>
 
 #include "entities/camera.hpp"
 #include "entities/world.hpp"
@@ -41,7 +40,7 @@ Text3d::~Text3d()
 
 
 Vec3
-Text3d::words_centroid(const std::vector<Text3dPtr>& words)
+Text3d::words_centroid(const DynArray<Text3dPtr>& words)
 {
     Vec3   c{};
     double n = 0.0;
diff --git a/src/text_3d/text_3d.hpp b/src/text_3d/text_3d.hpp
index 50c33b3..7825732 100644
--- a/src/text_3d/text_3d.hpp
+++ b/src/text_3d/text_3d.hpp
@@ -2,7 +2,6 @@
 
 #include <SDL3/SDL.h>
 #include <string>
-#include <vector>
 
 #include "cpputils/unique_ptr.hpp"
 #include "entities/game_object.hpp"
@@ -37,7 +36,7 @@ struct Text3d final : public GameObject {
     double ai_orbit_angle    = 0.0;       // current angle around player (radians)
     double ai_orbit_dir      = 1.0;       // +1 = clockwise, -1 = counter-clockwise
     bool   ai_has_stolen     = false;
-    std::vector<Text3dPtr>
+    DynArray<Text3dPtr>
            ai_stolen;
     Vec3   ai_trail_dir { 1.0, 0.0, 0.0 };
 
@@ -77,14 +76,14 @@ struct Text3d final : public GameObject {
     static inline constexpr float depth_wall     = 0.06f;
     static inline constexpr float depth_floor    = 0.14f;
     static inline constexpr float depth_compiler = 0.02f;
-    static Vec3 words_centroid(const std::vector<Text3dPtr>& words);
+    static Vec3 words_centroid(const DynArray<Text3dPtr>& words);
 
     std::string text;
     TextMesh    mesh;
 
     // Cached GPU-ready data, rebuilt only in rebuild_Mesh().
-    std::vector<float> packed_verts;   // interleaved pos(3)|normal(3)|isfront(3)
-    std::vector<int>   packed_indices;
+    DynArray<float> packed_verts;   // interleaved pos(3)|normal(3)|isfront(3)
+    DynArray<int>   packed_indices;
     mutable unsigned int gpu_vbo { 0 };  // owned GL buffer; 0 = not yet uploaded
     mutable unsigned int gpu_ebo { 0 };
     mutable int          gpu_index_count { 0 };
diff --git a/src/text_3d/text_label.cpp b/src/text_3d/text_label.cpp
index 24056a3..b363e0e 100644
--- a/src/text_3d/text_label.cpp
+++ b/src/text_3d/text_label.cpp
@@ -4,10 +4,10 @@
 #include "rendering/gl_renderer.hpp"
 #include "text_3d/text_label.hpp"
 
-static std::vector<std::string>
+static DynArray<std::string>
 split_words(const std::string& text)
 {
-    std::vector<std::string> out;
+    DynArray<std::string> out;
     std::istringstream ss(text);
     std::string word;
     while (ss >> word)
@@ -104,11 +104,11 @@ TextLabel::rebuild_(const std::string& text)
     jiggle_off_.clear();
     jiggle_vel_.clear();
 
-    std::vector<std::string> tokens = split_words(text);
+    DynArray<std::string> tokens = split_words(text);
     if (tokens.empty()) return;
 
     struct Tmp { Text3dPtr obj; };
-    std::vector<Tmp> tmp;
+    DynArray<Tmp> tmp;
     tmp.reserve(tokens.size());
     for (const std::string& tok : tokens) {
         tmp.push_back({ make_Text3d(tok, style_depth_, style_scale_,
diff --git a/src/text_3d/text_label.hpp b/src/text_3d/text_label.hpp
index 4cc1b62..37baa79 100644
--- a/src/text_3d/text_label.hpp
+++ b/src/text_3d/text_label.hpp
@@ -1,10 +1,10 @@
 #pragma once
 
 #include <string>
-#include <vector>
 
 #include "entities/camera.hpp"
 #include "text_3d/text_3d.hpp"
+#include "utils/globals.hpp"
 #include "utils/light.hpp"
 
 class GlRenderer;
@@ -37,18 +37,18 @@ public:
     void set_Collidable(bool on);
     void update(double dt, const Camera& camera);
     void draw(GlRenderer& gl, const Camera& camera, const Light& light) const;
-    const std::vector<Text3dPtr>&
+    const DynArray<Text3dPtr>&
          words() const { return words_; }
     void kick(size_t word_index, const Vec3& projectile_vel, const Vec3& normal);
 
     bool empty() const { return words_.empty(); }
 
 private:
-    std::vector<Text3dPtr> words_;
-    std::vector<double>    offsets_;
-    std::vector<Vec3>      anchor_pos_;
-    std::vector<Vec3>      jiggle_off_;
-    std::vector<Vec3>      jiggle_vel_;
+    DynArray<Text3dPtr> words_;
+    DynArray<double>    offsets_;
+    DynArray<Vec3>      anchor_pos_;
+    DynArray<Vec3>      jiggle_off_;
+    DynArray<Vec3>      jiggle_vel_;
 
     PositionMode  mode_           { PositionMode::Absolute };
     Vec3          centre_         {};
diff --git a/src/text_3d/text_mesh.cpp b/src/text_3d/text_mesh.cpp
index 5fdc916..ec35f5d 100644
--- a/src/text_3d/text_mesh.cpp
+++ b/src/text_3d/text_mesh.cpp
@@ -24,13 +24,13 @@ static constexpr int   BEZIER_STEPS = 8;
 static constexpr float FT_SCALE     = 1.0f / 64.0f;
 
 using Pt2  = std::array<float, 2>;
-using Ring = std::vector<Pt2>;
-using Poly = std::vector<Ring>;   // [0]=outer, [1..]=holes
+using Ring = DynArray<Pt2>;
+using Poly = DynArray<Ring>;   // [0]=outer, [1..]=holes
 
 // ── FreeType outline callbacks ────────────────────────────────────────────────
 
 struct OutlineCtx {
-    std::vector<Ring> rings;
+    DynArray<Ring> rings;
     Ring              current;
     Pt2               pen{};
     void flush() {
@@ -116,10 +116,10 @@ static bool aabbContains(const AABB& a, const AABB& b) {
 // Each CW (hole) ring belongs to the outer ring whose AABB contains it.
 // If no outer contains it, treat it as its own outer (handles edge cases).
 
-static std::vector<Poly> groupContours(std::vector<Ring>& rings, float pen_x, float scale)
+static DynArray<Poly> groupContours(DynArray<Ring>& rings, float pen_x, float scale)
 {
     struct RingInfo { Ring r; AABB bb; bool is_hole; };
-    std::vector<RingInfo> info;
+    DynArray<RingInfo> info;
     info.reserve(rings.size());
     for (auto& ring : rings) {
         Ring shifted;
@@ -132,12 +132,12 @@ static std::vector<Poly> groupContours(std::vector<Ring>& rings, float pen_x, fl
     }
 
     // Collect outers
-    std::vector<size_t> outer_ids, hole_ids;
+    DynArray<size_t> outer_ids, hole_ids;
     for (size_t i = 0; i < info.size(); i++)
         (info[i].is_hole ? hole_ids : outer_ids).push_back(i);
 
     // Each outer starts its own Poly
-    std::vector<Poly> polys;
+    DynArray<Poly> polys;
     for (size_t oi : outer_ids) {
         Poly p;
         p.push_back(info[oi].r);
@@ -236,16 +236,16 @@ bool buildTextMesh(TextMesh& out, const std::string& font_path,
         }
 
         // Group into outer+holes polygons, handling multi-contour glyphs ('i','j','!',...)
-        std::vector<Poly> polys = groupContours(ctx.rings, pen_x, scale);
+        DynArray<Poly> polys = groupContours(ctx.rings, pen_x, scale);
 
         for (Poly& poly : polys) {
             if (poly.empty() || poly[0].size() < 3) continue;
 
-            std::vector<uint32_t> indices = mapbox::earcut<uint32_t>(poly);
+            DynArray<uint32_t> indices = mapbox::earcut<uint32_t>(poly);
             if (indices.empty()) continue;
 
             // Flatten rings for vertex lookup
-            std::vector<Pt2> flat;
+            DynArray<Pt2> flat;
             for (auto& ring : poly)
                 for (auto& p : ring)
                     flat.push_back(p);
diff --git a/src/text_3d/text_mesh.hpp b/src/text_3d/text_mesh.hpp
index add61f0..5107bf9 100644
--- a/src/text_3d/text_mesh.hpp
+++ b/src/text_3d/text_mesh.hpp
@@ -1,6 +1,8 @@
 #pragma once
-#include <vector>
+
 #include <string>
+
+#include "utils/globals.hpp"
 #include "utils/math.hpp"
 
 struct Vertex {
@@ -11,10 +13,10 @@ struct Vertex {
 struct MeshTri { int a, b, c; };
 
 struct TextMesh {
-    std::vector<Vec3>    verts;
-    std::vector<MeshTri> tris;
-    std::vector<Vec3>    vertex_normals;
-    std::vector<float>   is_front;
+    DynArray<Vec3>    verts;
+    DynArray<MeshTri> tris;
+    DynArray<Vec3>    vertex_normals;
+    DynArray<float>   is_front;
 };
 
 bool buildTextMesh(
diff --git a/src/thirdparty/earcut/earcut.hpp b/src/thirdparty/earcut/earcut.hpp
index 3d24392..0c8a85b 100644
--- a/src/thirdparty/earcut/earcut.hpp
+++ b/src/thirdparty/earcut/earcut.hpp
@@ -20,8 +20,9 @@
 #include <limits>
 #include <memory>
 #include <utility>
-#include <vector>
-#include <cpputils/unique_ptr.hpp>
+
+#include "cpputils/unique_ptr.hpp"
+#include "utils/globals.hpp"
 
 
 namespace mapbox {
@@ -40,7 +41,7 @@ namespace detail {
 template <typename N = uint32_t>
 class Earcut {
 public:
-    std::vector<N> indices;
+    DynArray<N> indices;
     std::size_t vertices = 0;
 
     template <typename Polygon>
@@ -188,8 +189,8 @@ private:
             void operator()(T* ptr) { alloc_traits::deallocate(alloc, ptr, capacity); }
         };
 
-        std::vector<cpputils::unique_ptr<T, AllocDeleter>> memoryBlocks;
-        std::vector<std::size_t> blockCapacities;
+        DynArray<cpputils::unique_ptr<T, AllocDeleter>> memoryBlocks;
+        DynArray<std::size_t> blockCapacities;
         std::size_t currentBlockIndex = 0;
         std::size_t currentIndex = 0;
         std::size_t totalObjects = 0;
@@ -206,7 +207,7 @@ private:
     };
 
     cpputils::unique_ptr<ObjectPool<Node>> nodes;
-    std::vector<Node*> holeQueue;
+    DynArray<Node*> holeQueue;
 };
 
 template <typename N>
@@ -876,7 +877,7 @@ void Earcut<N>::removeNode(Node* p) {
 } // namespace detail
 
 template <typename N = uint32_t, typename Polygon>
-std::vector<N> earcut(const Polygon& poly) {
+DynArray<N> earcut(const Polygon& poly) {
     mapbox::detail::Earcut<N> earcut;
     earcut(poly);
     return std::move(earcut.indices);
diff --git a/src/utils/carry_math.cpp b/src/utils/carry_math.cpp
index 87fb06b..ca58122 100644
--- a/src/utils/carry_math.cpp
+++ b/src/utils/carry_math.cpp
@@ -14,14 +14,14 @@ CarryMath::line_Anchor(const Camera& camera, double carry_dist)
 }
 
 
-std::vector<Vec3>
+DynArray<Vec3>
 CarryMath::slot_positions(const CarriedLine& carried,
                                              const Vec3& anchor,
                                              const Vec3& right,
                                              double carry_gap)
 {
-    std::vector<Vec3> positions;
-    std::vector<CarriedWordNode*> nodes = carried.reverse_nodes();
+    DynArray<Vec3> positions;
+    DynArray<CarriedWordNode*> nodes = carried.reverse_nodes();
 
     if (nodes.empty()) return positions;
 
diff --git a/src/utils/carry_math.hpp b/src/utils/carry_math.hpp
index d6a8195..46667ea 100644
--- a/src/utils/carry_math.hpp
+++ b/src/utils/carry_math.hpp
@@ -1,8 +1,10 @@
 #pragma once
-#include <vector>
+
 #include <cstdint>
-#include "utils/math.hpp"
+
 #include "systems/carry_system.hpp"
+#include "utils/globals.hpp"
+#include "utils/math.hpp"
 
 class Camera;
 
@@ -14,7 +16,7 @@ namespace CarryMath
 
     Vec3   line_Anchor(const Camera& camera, double carry_dist);
 
-    std::vector<Vec3> slot_positions(const CarriedLine& carried,
+    DynArray<Vec3> slot_positions(const CarriedLine& carried,
                                      const Vec3& anchor,
                                      const Vec3& right,
                                      double carry_gap);
diff --git a/src/utils/globals.hpp b/src/utils/globals.hpp
index fde9f32..b32df99 100644
--- a/src/utils/globals.hpp
+++ b/src/utils/globals.hpp
@@ -14,9 +14,12 @@ struct TextWall;
 struct GameObject;
 struct CarriedWordNode;
 
-using GlRendererPtr = cpputils::unique_ptr<GlRenderer>;
-using Text3dPtr     = cpputils::unique_ptr<Text3d>;
-using TextWallPtr   = cpputils::unique_ptr<TextWall>;
-using TextWallList  = std::vector<TextWallPtr>;
+template<typename T> 
+using DynArray           = std::vector<T>;
+
+using GlRendererPtr      = cpputils::unique_ptr<GlRenderer>;
+using Text3dPtr          = cpputils::unique_ptr<Text3d>;
+using TextWallPtr        = cpputils::unique_ptr<TextWall>;
+using TextWallList       = DynArray<TextWallPtr>;
 using CarriedWordNodePtr = cpputils::unique_ptr<CarriedWordNode>;
 using GameObjectPtr      = cpputils::unique_ptr<GameObject>;
diff --git a/src/utils/text_wrap.hpp b/src/utils/text_wrap.hpp
index 53b15af..93863a1 100644
--- a/src/utils/text_wrap.hpp
+++ b/src/utils/text_wrap.hpp
@@ -1,6 +1,8 @@
 #pragma once
+
 #include <string>
-#include <vector>
+
+#include "utils/globals.hpp"
 
 // ─────────────────────────────────────────────────────────────────────────
 // wrap_Text — break `text` into lines no longer than `max_chars`.
@@ -13,10 +15,10 @@
 //   - a single word longer than max_chars is NOT split — it occupies its
 //     own (over-length) line, same as the original
 // ─────────────────────────────────────────────────────────────────────────
-inline std::vector<std::string>
+inline DynArray<std::string>
 wrap_Text(const std::string& text, size_t max_chars)
 {
-    std::vector<std::string> lines;
+    DynArray<std::string> lines;
     std::string current_line;
     std::string word;
 
diff --git a/src/wall/editor_wall.cpp b/src/wall/editor_wall.cpp
index 2061dad..4f80b3c 100644
--- a/src/wall/editor_wall.cpp
+++ b/src/wall/editor_wall.cpp
@@ -130,8 +130,8 @@ EditorWall::draw(GlRenderer& gl, const Camera& cam, const Light& light) const
 {
     if (lines_.empty()) return;
 
-    std::vector<float> verts;
-    std::vector<int>   indices;
+    DynArray<float> verts;
+    DynArray<int>   indices;
     verts.reserve(lines_.size() * 4 * 9);
     indices.reserve(lines_.size() * 6);
 
diff --git a/src/wall/editor_wall.hpp b/src/wall/editor_wall.hpp
index d290a50..0f21ffc 100644
--- a/src/wall/editor_wall.hpp
+++ b/src/wall/editor_wall.hpp
@@ -1,8 +1,7 @@
 #pragma once
 
-#include <vector>
-
 #include "text_3d/text_3d.hpp"
+#include "utils/globals.hpp"
 #include "utils/light.hpp"
 #include "utils/math.hpp"
 #include "wall.hpp"
@@ -32,7 +31,7 @@ class EditorWall
 {
     struct Line {
         double                 y      {0.0};
-        std::vector<Text3d*>   words;            // left-to-right
+        DynArray<Text3d*>   words;            // left-to-right
     };
 
     // Result of placing a word: the final Z, and the index in the line's
@@ -42,7 +41,7 @@ class EditorWall
         size_t insert_index;
     };
 
-    std::vector<Line> lines_;
+    DynArray<Line> lines_;
     Line* find_NearestLine(double y);
 
     // Decide where the new word lands on the line and shift neighbours if a
diff --git a/src/wall/text_wall.cpp b/src/wall/text_wall.cpp
index e4ffa78..c6c08aa 100644
--- a/src/wall/text_wall.cpp
+++ b/src/wall/text_wall.cpp
@@ -47,7 +47,7 @@ TextWall::from_Drop(Text3dPtr src, float world_scale)
 std::string
 TextWall::extract_Text(const TextWallList& texts, Wall::Role role, double line_threshold)
 {
-    std::vector<TextWall*> items;
+    DynArray<TextWall*> items;
     items.reserve(texts.size());
 
     for (const TextWallPtr& wt : texts) {
@@ -66,11 +66,11 @@ TextWall::extract_Text(const TextWallList& texts, Wall::Role role, double line_t
         });
 
     struct Line {
-        std::vector<TextWall*> items;
+        DynArray<TextWall*> items;
         double sum_y = 0.0;
     };
 
-    std::vector<Line> lines;
+    DynArray<Line> lines;
     lines.reserve(items.size());
 
     for (TextWall* wt : items) {
diff --git a/src/wall/text_wall_scroll.hpp b/src/wall/text_wall_scroll.hpp
index e1838be..60b1f8b 100644
--- a/src/wall/text_wall_scroll.hpp
+++ b/src/wall/text_wall_scroll.hpp
@@ -37,7 +37,7 @@ namespace TextWallScroll
     // They are moved out of wall_texts and into floor_objects with Dynamic physics.
     inline void
     clear_ToFloor(TextWallList& texts,
-                   std::vector<Text3dPtr>& words,
+                   DynArray<Text3dPtr>& words,
                    Wall::Role role,
                    double floor_y)
     {
diff --git a/src/wall/wall_surfaces.hpp b/src/wall/wall_surfaces.hpp
index 0a723c6..0057e3d 100644
--- a/src/wall/wall_surfaces.hpp
+++ b/src/wall/wall_surfaces.hpp
@@ -1,9 +1,9 @@
 #pragma once
 
 #include <SDL3/SDL.h>
-#include <vector>
 
 #include "rendering/gl_renderer.hpp"
+#include "utils/globals.hpp"
 #include "utils/light.hpp"
 #include "utils/math.hpp"
 #include "wall/wall.hpp"
@@ -25,8 +25,8 @@ class WallSurfaces
 public:
     struct WallQuad {
         Wall::Role role;
-        std::vector<float> verts;
-        std::vector<int>   indices;
+        DynArray<float> verts;
+        DynArray<int>   indices;
         SDL_FColor         color;
         bool               is_editor;
     };
@@ -35,6 +35,6 @@ public:
     void draw(GlRenderer& gl, const Camera& cam, const Light& light) const;
 
 private:
-    std::vector<WallQuad> walls_;
+    DynArray<WallQuad> walls_;
 };