diff --git a/include/maker/ocean.hpp b/include/maker/ocean.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..1788316101652da317d2dafcb054fcd8f2f477aa
--- /dev/null
+++ b/include/maker/ocean.hpp
@@ -0,0 +1,59 @@
+#ifndef MAKER_OCEAN_HPP_INCLUDED
+#   define MAKER_OCEAN_HPP_INCLUDED
+
+#   include <gfx/buffer.hpp>
+#   include <math/math.hpp>
+
+namespace mak {
+
+class Ocean
+{
+public:
+    Ocean(std::uint8_t dimension, scalar amplitude, scalar gravity, vec2 const& wind, scalar length)
+        : m_dimension { dimension }
+        , m_buffer_size { dimension + 1U }
+        , m_amplitude { amplitude }
+        , m_gravity { gravity }
+        , m_wind { wind }
+        , m_wind_length { len(wind) }
+        , m_length { length }
+        , m_positions { }
+        , m_normals { }
+    {
+        m_positions.resize(m_buffer_size * m_buffer_size);
+        m_normals.resize(m_buffer_size * m_buffer_size);
+    }
+
+    gfx::Buffer::PositionsVec const& positions() const { return m_positions; }
+    gfx::Buffer::PositionsVec const& normals() const { return m_normals; }
+
+    void update_buffers(scalar t);
+
+    ~Ocean() {}
+
+private:
+    vec2 wave_number(vec2i const& coords) const;
+    scalar dispersion(scalar wave_length) const;
+    scalar philips_spectrum(vec2 const& k, scalar wave_length) const;
+
+    void update_point(vec2i const& coords, scalar t);
+
+    vec2 wave_height(vec2 const& k, scalar wave_length) const;
+    vec2 wave_height(vec2 const& k, scalar wave_length, scalar t) const;
+
+    gfx::Buffer::PositionsVec m_positions;
+    gfx::Buffer::NormalsVec m_normals;
+
+    std::uint8_t const m_dimension;
+    std::uint8_t const m_buffer_size;
+    scalar const m_amplitude;
+    scalar const m_gravity;
+    vec2 const m_wind;
+    scalar const m_wind_length;
+    scalar const m_length;
+
+};
+
+}
+
+#endif
diff --git a/include/maker/updater.hpp b/include/maker/updater.hpp
index 1f9ce8a9c6b52a7ae7872139a9be271c600500b2..f6e5692b8c65d8c8d12adb9d82a15642d0403ef1 100644
--- a/include/maker/updater.hpp
+++ b/include/maker/updater.hpp
@@ -2,6 +2,7 @@
 #   define MAKER_UPDATER_HPP_INCLUDED
 
 #   include <com/runner.hpp>
+#   include <maker/ocean.hpp>
 #   include <math/math.hpp>
 
 namespace com { struct Frame; }
@@ -19,6 +20,11 @@ struct Updater : public com::Runner
 
 protected:
 
+    Ocean ocean {16U, 2.0f, 9.81f, { 2.0f, 3.0f }, 5.0f };
+
+    gfx::Buffer* plane_buffer = nullptr;
+    gfx::Buffer::PositionsVec default_plane_positions;
+
     void initialize() override;
     void release() override;
 };
diff --git a/src/ocean.cpp b/src/ocean.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..a40514b031c49e90c1ed71fd7eef9c0171e5997e
--- /dev/null
+++ b/src/ocean.cpp
@@ -0,0 +1,82 @@
+#include <maker/ocean.hpp>
+
+namespace mak {
+
+void Ocean::update_buffers(scalar t) 
+{
+    for (std::uint8_t x = 0; x < m_dimension; ++x)
+        for (std::uint8_t y = 0; y < m_dimension; ++y)
+            update_point(vec2i(x, y), t);
+}
+
+vec2 Ocean::wave_number(vec2i const& coords) const
+{
+    return vec2(
+        Num::pi * (2.0f * get<0>(coords) - m_dimension) / m_length,
+        Num::pi * (2.0f * get<1>(coords) - m_dimension) / m_length  
+    );
+}
+
+scalar Ocean::dispersion(scalar wave_length) const
+{
+    return std::sqrtf(this->m_gravity * wave_length);
+}
+
+scalar Ocean::philips_spectrum(vec2 const& k, scalar wave_length) const
+{
+    if (wave_length < 1e-4f) return 0.0f;
+
+    scalar L = m_wind_length * m_wind_length / m_gravity;
+    scalar kdotw = dot(k, m_wind);
+    scalar k4 = wave_length * wave_length * wave_length * wave_length;
+    scalar middle = std::expf(-1 / ((wave_length * L) * (wave_length * L))) / k4;
+    return m_amplitude * middle * kdotw * kdotw;
+}
+
+void Ocean::update_point(vec2i const& coords, scalar t) 
+{
+    vec3 displacement;
+    for (std::uint8_t x = 0U; x < m_dimension; ++x)
+        for (std::uint8_t y = 0U; y < m_dimension; ++y) 
+        {
+            // Can be quickly precalculated into a vector as most of the stuff
+            vec2 k = wave_number(vec2i(x, y));
+            scalar wave_length = len(k);
+
+            if (wave_length < 1e-4f) continue;
+
+            scalar wave_number_dot_coords = dot(k, coords);
+            vec2 xy = vec2(x, y);
+            vec2 height = 
+                wave_height(xy, len(xy), t) * 
+                vec2(
+                    std::cosf(wave_number_dot_coords), 
+                    std::sinf(wave_number_dot_coords)
+                );
+
+            vec2 xy_displacement = normalized(k) * get<1>(height);
+            displacement += vec3(-get<0>(xy_displacement), -get<1>(xy_displacement), get<0>(height));
+        }
+
+    m_positions[get<0>(coords) * m_dimension + get<1>(coords)] = displacement;
+}
+
+vec2 Ocean::wave_height(vec2 const& k, scalar wave_length) const
+{
+    scalar rand1 = 0.5f;
+    scalar rand2 = 0.5f;
+    vec2 first = vec2{ (1.0f / std::sqrtf(2.0f)) * std::sqrtf(philips_spectrum(k, wave_length)) };
+    return first * vec2(rand1, rand2);
+}
+
+vec2 Ocean::wave_height(vec2 const& k, scalar wave_length, scalar t) const
+{
+    scalar omegat = dispersion(wave_length) * t;
+    scalar cosine = std::cosf(omegat);
+    scalar sinus = std::sinf(omegat);
+    vec2 first = wave_height(k, wave_length) * vec2(cosine, sinus);
+    vec2 second = wave_height(-k, wave_length) * vec2(cosine, -sinus);
+    return first + vec2(get<0>(second), -get<1>(second));
+}
+
+}
\ No newline at end of file
diff --git a/src/presenter.cpp b/src/presenter.cpp
index c6ad96ef136b79efc2669e99784b77e01d6fc249..3c8aa84a9c47edbe70837ff6c11cd877881f01ac 100644
--- a/src/presenter.cpp
+++ b/src/presenter.cpp
@@ -46,6 +46,20 @@ void Presenter::initialize()
 
     // --- END test_box ---
 
+    // --- BEGIN plane ---
+
+    com::Folder* const test_plane = gfx::object_system()->insert_object(
+            { "maker", "test_plane" },
+            gfx::material_system()->insert_material("plane_material", gfx::shader_system()->forward_unlit_vertex_color_shader(), { "maker" }),
+            gfx::buffer_generators()->insert_procedural_plane({ 0.5f, 0.5f }, 17U, 17U, "test_plane", { "maker" })
+            );
+
+    com::Frame* const frame3 = root()->push_back<com::Folder>("test_plane")->push_back<com::Frame>();
+    frame3->move_origin({ 1.0f, 1.0f, 0.0f });
+    gfx::object_system()->push_frame_back(test_plane, frame3);
+
+    // --- END plane ---
+
     // --- BEGIN directional light ---
 
     com::Frame* const frame_light = root()->push_back<com::Folder>("directional_light")->push_back<com::Frame>();
@@ -66,6 +80,7 @@ void Presenter::release()
     osi::presenters()->find<com::Folder>("maker")->erase(self_name() + ".link");
 
     root()->erase("test_box");
+    root()->erase("test_plane");
 }
 
 void Presenter::next_round()
diff --git a/src/updater.cpp b/src/updater.cpp
index 63502e7f0b095d79d0d6ca34a156249ef2f5a08a..d547dded85fa92d24ec05557ed91acb14fedf43a 100644
--- a/src/updater.cpp
+++ b/src/updater.cpp
@@ -29,35 +29,56 @@ void Updater::release()
     osi::updaters()->find<com::Folder>("maker")->erase(self_name() + ".link");
 }
 
-void Updater::next_round()
+static void camera_movement()
 {
+    scalar const linear_speed = 10.0f;
+    basis3 const camera_basis{ to_mat3x3(camera_frame()->frame().rotation()) };
+    if (osi::keyboard()->down().contains("W") || osi::keyboard()->down().contains("Up"))
+        camera_frame()->move_origin((-linear_speed * osi::timer()->dt()) * camera_basis.axis<2>());
+    else if (osi::keyboard()->down().contains("S") || osi::keyboard()->down().contains("Down"))
+        camera_frame()->move_origin((linear_speed * osi::timer()->dt()) * camera_basis.axis<2>());
+
+    if (osi::keyboard()->down().contains("A") || osi::keyboard()->down().contains("Left"))
+        camera_frame()->move_origin((-linear_speed * osi::timer()->dt()) * camera_basis.axis<0>());
+    else if (osi::keyboard()->down().contains("D") || osi::keyboard()->down().contains("Right"))
+        camera_frame()->move_origin((linear_speed * osi::timer()->dt()) * camera_basis.axis<0>());
+
+    if (osi::keyboard()->down().contains("Q"))
+        camera_frame()->move_origin((-linear_speed * osi::timer()->dt()) * camera_basis.axis<1>());
+    else if (osi::keyboard()->down().contains("E"))
+        camera_frame()->move_origin((linear_speed * osi::timer()->dt()) * camera_basis.axis<1>());
+
     if (osi::mouse()->down().contains("MouseRight"))
     {
-        scalar const linear_speed = 5.0f;
-        scalar const rotation_speed_mult = 5.0f;
+        scalar const rotation_speed_mult = 10.0f;
         vec2 const mouse_angle =
             (rotation_speed_mult / gfx::camera_system()->default_camera()->near()) *
             (vec2{ osi::mouse()->pos_delta() } * osi::window()->pixel_size_in_meters());
 
-        basis3 const camera_basis{ to_mat3x3(camera_frame()->frame().rotation()) };
-        if (osi::keyboard()->down().contains("W") || osi::keyboard()->down().contains("Up"))
-            camera_frame()->move_origin((-linear_speed * osi::timer()->dt()) * camera_basis.axis<2>());
-        else if (osi::keyboard()->down().contains("S") || osi::keyboard()->down().contains("Down"))
-            camera_frame()->move_origin((linear_speed * osi::timer()->dt()) * camera_basis.axis<2>());
-
-        if (osi::keyboard()->down().contains("A") || osi::keyboard()->down().contains("Left"))
-            camera_frame()->move_origin((-linear_speed * osi::timer()->dt()) * camera_basis.axis<0>());
-        else if (osi::keyboard()->down().contains("D") || osi::keyboard()->down().contains("Right"))
-            camera_frame()->move_origin((linear_speed * osi::timer()->dt()) * camera_basis.axis<0>());
-
-        if (osi::keyboard()->down().contains("Q"))
-            camera_frame()->move_origin((-linear_speed * osi::timer()->dt()) * camera_basis.axis<1>());
-        else if (osi::keyboard()->down().contains("E"))
-            camera_frame()->move_origin((linear_speed * osi::timer()->dt()) * camera_basis.axis<1>());
-
         camera_frame()->move_rotation(to_quat(-get<1>(mouse_angle), camera_basis.axis<0>()));
         camera_frame()->move_rotation(to_quat(-get<0>(mouse_angle), axis<vec3, 2>()));
     }
 }
 
+void Updater::next_round()
+{
+    camera_movement();
+
+    // if (plane_buffer == nullptr) {
+    //     plane_buffer = gfx::buffer_system()->folder()->find<com::Folder>("buffers")->find<com::Folder>("maker")->find<gfx::Buffer>("test_plane");
+    //     default_plane_positions = (*plane_buffer->positions());
+    // }
+
+    // ocean.update_buffers(osi::timer()->dt());
+    // plane_buffer->erase_positions();
+    // gfx::Buffer::PositionsVec new_positions = default_plane_positions;
+    // for (std::size_t i = 0; i < ocean.positions().size() - 1; ++i) {
+    //     new_positions[i * 4U] += ocean.positions()[i] / vec3(1.0, 1.0, 100.0);
+    //     new_positions[i * 4U + 1U] += ocean.positions()[i + 1U] / vec3(1.0, 1.0, 100.0);
+    //     new_positions[i * 4U + 2U] += ocean.positions()[i] / vec3(1.0, 1.0, 100.0);
+    //     new_positions[i * 4U + 3U] += ocean.positions()[i] / vec3(1.0, 1.0, 100.0);
+    // }
+    // plane_buffer->insert_positions(new_positions);
+}
+
 }