diff --git a/c/gtk3/gravity/gravity.c b/c/gtk3/gravity/gravity.c
new file mode 100644
index 0000000..2198897
--- /dev/null
+++ b/c/gtk3/gravity/gravity.c
@@ -0,0 +1,141 @@
+#include <math.h>
+#include <stdlib.h>
+#include <time.h>
+#include <gtk/gtk.h>
+
+
+#define BALLS_COUNT 5
+
+const double pi = 3.141592653589793;
+const double g = 100.0;
+const guint interval_ms = 10;
+
+typedef struct {
+  double x, y, vx, vy, ax, ay, r, m;
+} Ball;
+
+Ball balls[BALLS_COUNT];
+double ring_x = 500.0;
+double ring_y = 500.0;
+
+GtkWidget *window;
+
+
+double ring_normalize(double a, double ring_size) {
+  if (!ring_size) return a;
+  return a - ring_size*floor(a/ring_size);
+}
+
+double ring_delta(double a, double b, double ring_size) {
+  if (!ring_size) return a - b;
+  double delta = ring_normalize(a - b, ring_size);
+  if (delta > ring_size/2) delta -= ring_size;
+  return delta;
+}
+
+gboolean timeout(gpointer data) {
+  // draw do 100 frames per seconds (see interval_ms)
+  // and 1000 calculation cycles per each frame
+  double dt = (double)interval_ms/1000.0/1000.0;
+  for(int t = 0; t < 1000; ++t) {
+    // reset accelerations
+    for(int i = 0; i < BALLS_COUNT; ++i) {
+      balls[i].ax = 0;
+      balls[i].ay = 0;
+    }
+
+    // accumulate accelerations and detect collisions
+    for(int i = 0; i < BALLS_COUNT; ++i) {
+      for(int j = i+1; j < BALLS_COUNT; ++j) {
+        double dx = ring_delta(balls[j].x, balls[i].x, ring_x);
+        double dy = ring_delta(balls[j].y, balls[i].y, ring_y);
+        double d = sqrt(dx*dx + dy*dy);
+        double nx = dx/d;
+        double ny = dy/d;
+        if (d > balls[i].r + balls[j].r) {
+          // balls don't touches, just apply gravity acceleration
+          double ax = g*balls[i].m*balls[j].m*nx/(d*d);
+          double ay = g*balls[i].m*balls[j].m*ny/(d*d);
+          balls[i].ax = balls[i].ax + ax;
+          balls[i].ay = balls[i].ay + ay;
+          balls[j].ax = balls[j].ax - ax;
+          balls[j].ay = balls[j].ay - ay;
+        } else {
+          // collision detected
+          double dvx = balls[j].vx - balls[i].vx;
+          double dvy = balls[j].vy - balls[i].vy;
+          double dv = dvx*nx + dvy*ny;
+          if (dv < 0) {
+            // calc velocities after ricochet, if balls still flies towards each other
+            double v0 =  2.0*dv*balls[j].m/(balls[i].m + balls[j].m);
+            double v1 = -2.0*dv*balls[i].m/(balls[i].m + balls[j].m);
+            balls[i].vx = balls[i].vx + v0*nx;
+            balls[i].vy = balls[i].vy + v0*ny;
+            balls[j].vx = balls[j].vx + v1*nx;
+            balls[j].vy = balls[j].vy + v1*ny;
+          }
+        }
+      }
+    }
+    
+    // apply accelerations
+    for(int i = 0; i < BALLS_COUNT; ++i) {
+      balls[i].vx = balls[i].vx + balls[i].ax*dt/balls[i].m;
+      balls[i].vy = balls[i].vy + balls[i].ay*dt/balls[i].m;
+      balls[i].x = ring_normalize(balls[i].x + balls[i].vx*dt, ring_x);
+      balls[i].y = ring_normalize(balls[i].y + balls[i].vy*dt, ring_y);
+    }
+  }
+
+  gtk_widget_queue_draw(window);
+  return TRUE;
+}
+
+gboolean draw(GtkWidget *widget, cairo_t *cr, gpointer data) {
+  cairo_set_source_rgba(cr, 0.9, 0.9, 0.9, 1);
+  cairo_paint(cr);
+
+  cairo_set_source_rgba(cr, 0, 0, 0, 1);
+  cairo_set_line_width(cr, 1.0);
+  for(int i = 0; i < BALLS_COUNT; ++i) {
+    double r = balls[i].r;
+    for(int j = -1; j <= 1; ++j) {
+      for(int k = -1; k <= 1; ++k) {
+        double x = balls[i].x + ring_x*j;
+        double y = balls[i].y + ring_y*k;
+        cairo_arc(cr, x, y, r, 0, 2*pi);
+        cairo_stroke(cr);
+      }
+    }
+  }
+  return TRUE;
+}
+
+void activate(GtkApplication* app, gpointer data) {
+  srand(time(NULL));
+  for(int i = 0; i < BALLS_COUNT; ++i) {
+    double r = 10.0 + 30.0*(rand()/(double)(RAND_MAX));
+    balls[i].x = ring_x*rand()/(double)(RAND_MAX);
+    balls[i].y = ring_y*rand()/(double)(RAND_MAX);
+    balls[i].vx = 0.0;
+    balls[i].vy = 0.0;
+    balls[i].r = r;
+    balls[i].m = r*r*r;
+  }
+
+  window = gtk_application_window_new (app);
+  g_signal_connect(window, "draw", G_CALLBACK(draw), NULL);
+  gtk_window_set_default_size(GTK_WINDOW(window), (int)ring_x, (int)ring_y);
+  gtk_widget_show_all(window);
+
+  g_timeout_add(interval_ms, timeout, NULL);
+}
+
+int main(int argc, char **argv) {
+  GtkApplication *application = gtk_application_new(NULL, 0);
+  g_signal_connect(application, "activate", G_CALLBACK(activate), NULL);
+  int status = g_application_run(G_APPLICATION(application), argc, argv);
+  g_object_unref(application);
+  return status;
+}
+