diff --git a/water.c b/water.c
new file mode 100644
index 0000000..fa9d7e6
--- /dev/null
+++ b/water.c
@@ -0,0 +1,174 @@
+
+#include <math.h>
+#include <stdlib.h>
+#include <helianthus.h>
+
+
+#define SIZE 100
+
+
+double matrix[3][2] = {
+  { 5,  2 },
+  { 5, -2 },
+  { 0, -5 } };
+
+double invMatrix[2][2] = {
+  { 0.1 ,  0.1  },
+  { 0.25, -0.25 } };
+
+
+
+double grid[2][SIZE][SIZE];
+int gridId = 0;
+double simTm;
+
+
+double calcX(double x, double y, double z)
+  { return matrix[0][0]*x + matrix[1][0]*y + matrix[2][0]*z; }
+double calcY(double x, double y, double z)
+  { return matrix[0][1]*x + matrix[1][1]*y + matrix[2][1]*z; }
+
+double calcBackX(double sx, double sy)
+  { return invMatrix[0][0]*sx + invMatrix[1][0]*sy; }
+double calcBackY(double sx, double sy)
+  { return invMatrix[0][1]*sx + invMatrix[1][1]*sy; }
+
+
+void drawBox(double x, double y, double z, double dx, double dy, double dz, unsigned int color) {
+  saveState();
+  double ch = colorGetHue(color);
+  double cs = colorGetSaturation(color);
+  double cv = colorGetValue(color);
+
+  noStroke();
+
+  fill(colorByHSV(ch, cs, cv*0.6));
+  moveTo( calcX(x, y, z),
+          calcY(x, y, z) );
+  lineTo( calcX(x+dx, y, z),
+          calcY(x+dx, y, z) );
+  lineTo( calcX(x+dx, y, z+dz),
+          calcY(x+dx, y, z+dz) );
+  lineTo( calcX(x, y, z+dz),
+          calcY(x, y, z+dz) );
+  closePath();
+
+
+  fill(colorByHSV(ch, cs, cv*0.8));
+  moveTo( calcX(x+dx, y, z),
+          calcY(x+dx, y, z) );
+  lineTo( calcX(x+dx, y+dy, z),
+          calcY(x+dx, y+dy, z) );
+  lineTo( calcX(x+dx, y+dy, z+dz),
+          calcY(x+dx, y+dy, z+dz) );
+  lineTo( calcX(x+dx, y, z+dz),
+          calcY(x+dx, y, z+dz) );
+  closePath();
+
+
+  fill(color);
+  moveTo( calcX(x, y, z+dz),
+          calcY(x, y, z+dz) );
+  lineTo( calcX(x+dx, y, z+dz),
+          calcY(x+dx, y, z+dz) );
+  lineTo( calcX(x+dx, y+dy, z+dz),
+          calcY(x+dx, y+dy, z+dz) );
+  lineTo( calcX(x, y+dy, z+dz),
+          calcY(x, y+dy, z+dz) );
+  closePath();
+
+
+  restoreState();
+}
+
+
+void setPoint(int i, int j, int size, double z) {
+  for(int ii = -size; ii <= size; ++ii)
+    for(int jj = -size; jj <= size; ++jj)
+      if ( i + ii >= 0 && i + ii < SIZE
+        && j + jj >= 0 && j + jj < SIZE
+        && ii*ii + jj*jj <= size*(size + 1) )
+          grid[0][i+ii][j+jj] = grid[1][i+ii][j+jj] = z;
+}
+
+
+void init() { }
+
+
+void draw() {
+  double w = windowGetWidth();
+  double h = windowGetHeight();
+  double tm = windowGetSeconds();
+
+  saveState();
+  translate(w/2, h/2);
+
+  double smx = mouseTransformedX();
+  double smy = mouseTransformedY();
+  double mx = calcBackX(smx, smy);
+  double my = calcBackY(smx, smy);
+
+  if (keyWentDown("space"))
+    setPoint(randomNumber(0, SIZE-1), randomNumber(0, SIZE-1), 2, (randomFloat() - 0.5)*10);
+
+  double k = 1/sqrt(2);
+  double dt = 0.01;
+  if (fabs(simTm - tm) > 5) simTm = tm - dt/2;
+  while(simTm < tm) {
+    if (mouseDown("left") || mouseDown("right") || mouseDown("middle")) {
+      int i = (int)floor(mx + SIZE*0.5);
+      int j = (int)floor(my + SIZE*0.5);
+      setPoint(i, j, 4, mouseDown("left") ? 20 : mouseDown("right") ? -20 : 0);
+    }
+
+    int prevId = gridId;
+    gridId = 1 - gridId;
+    for(int i = 0; i < SIZE; ++i) {
+      for(int j = 0; j < SIZE; ++j) {
+        double z = grid[prevId][i][j];
+        double v = z - grid[gridId][i][j];
+
+        double f = 0;
+        double sum = 0;
+        if (i > 0     ) { f += grid[prevId][i-1][j] - z; sum += 1; }
+        if (i < SIZE-1) { f += grid[prevId][i+1][j] - z; sum += 1; }
+        if (j > 0     ) { f += grid[prevId][i][j-1] - z; sum += 1; }
+        if (j < SIZE-1) { f += grid[prevId][i][j+1] - z; sum += 1; }
+        if (i > 0      && j > 0     ) { f += k*(grid[prevId][i-1][j-1] - z); sum += k; }
+        if (i < SIZE-1 && j > 0     ) { f += k*(grid[prevId][i+1][j-1] - z); sum += k; }
+        if (i > 0      && j < SIZE-1) { f += k*(grid[prevId][i-1][j+1] - z); sum += k; }
+        if (i < SIZE-1 && j < SIZE-1) { f += k*(grid[prevId][i+1][j+1] - z); sum += k; }
+        if (sum > 0) f /= sum;
+
+        grid[gridId][i][j] = z + (v + 0.1*f)*0.99;
+      }
+    }
+    simTm += dt;
+  }
+
+  for(int i = 0; i < SIZE; ++i)
+    for(int j = SIZE-1; j >= 0; --j)
+      drawBox( i - SIZE*0.5 - 0.5,
+               j - SIZE*0.5 - 0.5,
+               -50,
+               1, 1, 50 + grid[gridId][i][j],
+               COLOR_AQUA );
+
+  restoreState();
+
+  saveState();
+  noFill();
+  text(10, 10, "try left and right mouse buttons");
+  restoreState();
+}
+
+
+int main() {
+  windowSetTitle("Water");
+  windowSetVariableFrameRate();
+  windowSetResizable(TRUE);
+  windowSetInit(&init);
+  windowSetDraw(&draw);
+  windowRun();
+  return 0;
+}