diff --git a/data/sound/beep.ogg b/data/sound/beep.ogg
new file mode 100644
index 0000000..71f5128
Binary files /dev/null and b/data/sound/beep.ogg differ
diff --git a/data/sound/nom.ogg b/data/sound/nom.ogg
new file mode 100644
index 0000000..3a55a7e
Binary files /dev/null and b/data/sound/nom.ogg differ
diff --git a/data/sprite/apple.png b/data/sprite/apple.png
new file mode 100644
index 0000000..53405d8
Binary files /dev/null and b/data/sprite/apple.png differ
diff --git a/data/sprite/breadball.png b/data/sprite/breadball.png
new file mode 100644
index 0000000..da1cba7
Binary files /dev/null and b/data/sprite/breadball.png differ
diff --git a/data/sprite/bricks.png b/data/sprite/bricks.png
new file mode 100644
index 0000000..fe10e29
Binary files /dev/null and b/data/sprite/bricks.png differ
diff --git a/data/sprite/snake/bend.png b/data/sprite/snake/bend.png
new file mode 100644
index 0000000..6b67c61
Binary files /dev/null and b/data/sprite/snake/bend.png differ
diff --git a/data/sprite/snake/body.png b/data/sprite/snake/body.png
new file mode 100644
index 0000000..1b951c0
Binary files /dev/null and b/data/sprite/snake/body.png differ
diff --git a/data/sprite/snake/head.png b/data/sprite/snake/head.png
new file mode 100644
index 0000000..54ded56
Binary files /dev/null and b/data/sprite/snake/head.png differ
diff --git a/data/sprite/snake/tail.png b/data/sprite/snake/tail.png
new file mode 100644
index 0000000..65a83a1
Binary files /dev/null and b/data/sprite/snake/tail.png differ
diff --git a/snake.c b/snake.c
new file mode 100644
index 0000000..7df970d
--- /dev/null
+++ b/snake.c
@@ -0,0 +1,362 @@
+
+#include <helianthus.h>
+
+#define SIZE 32
+#define WIDTH 16
+#define HEIGHT 16
+#define MAXLEN (WIDTH*HEIGHT+1)
+#define MAXFOOD 5
+#define FRAMERATE 24
+
+
+enum TileType {
+  NONE,
+  WALL,
+  FOOD,
+  SNAKE
+};
+
+enum GameMode {
+  MENU,
+  START,
+  PLAY,
+  GAMEOVER
+};
+
+
+int grid[WIDTH][HEIGHT];
+Sprite sprites[WIDTH][HEIGHT];
+Group gridGroup, storage;
+
+Sound nom, cut;
+
+int snake[MAXLEN][2];
+int len;
+int foodCount;
+int dx, dy;
+int framesRemain;
+
+int optStop;
+int optCut;
+double optWalls;
+int optStepFrames;
+
+int difficulty;
+
+int mode;
+
+char *tileWall = "data/sprite/bricks.png";
+char *tileFood = "data/sprite/apple.png";
+char *tileHead = "data/sprite/snake/head.png";
+char *tileBody = "data/sprite/snake/body.png";
+char *tileBend = "data/sprite/snake/bend.png";
+char *tileTail = "data/sprite/snake/tail.png";
+
+
+void putFood() {
+  for(int i = 0; i < 100 && foodCount < MAXFOOD; ++i) {
+    int x = randomNumber(0, WIDTH-1);
+    int y = randomNumber(0, HEIGHT-1);
+    if (grid[x][y] == NONE) {
+      grid[x][y] = FOOD;
+      spriteSetAnimation(sprites[x][y], tileFood);
+      ++foodCount;
+    }
+  }
+}
+
+
+void setSnakeTile(int i) {
+  int x = snake[i][0];
+  int y = snake[i][1];
+  Sprite s = sprites[x][y];
+  char *tile = tileBody;
+  double rotation = 0;
+
+  int left   = FALSE;
+  int right  = FALSE;
+  int top    = FALSE;
+  int bottom = FALSE;
+  if (i > 0) {
+    left   = left   || snake[i-1][0] < x;
+    right  = right  || snake[i-1][0] > x;
+    top    = top    || snake[i-1][1] < y;
+    bottom = bottom || snake[i-1][1] > y;
+  }
+  if (i < len-1) {
+    left   = left   || snake[i+1][0] < x;
+    right  = right  || snake[i+1][0] > x;
+    top    = top    || snake[i+1][1] < y;
+    bottom = bottom || snake[i+1][1] > y;
+  }
+
+  if (i == 0) {
+    tile = tileHead;
+    if (top)    rotation =  90;
+    if (bottom) rotation = -90;
+    if (right)  rotation = 180;
+  } else
+  if (i == len-1) {
+    tile = tileTail;
+    if (top)    rotation = -90;
+    if (bottom) rotation =  90;
+    if (left)   rotation = 180;
+  } else
+  if (left && top) {
+    tile = tileBend;
+    rotation = 180;
+  } else
+  if (left && bottom) {
+    tile = tileBend;
+    rotation = 90;
+  } else
+  if (right && top) {
+    tile = tileBend;
+    rotation = -90;
+  } else
+  if (right && bottom) {
+    tile = tileBend;
+    rotation = 0;
+  } else
+  if (top) {
+    rotation = 90;
+  }
+
+  spriteSetAnimation(s, tile);
+  spriteSetRotation(s, rotation);
+}
+
+
+void resetTile(int x, int y) {
+  grid[x][y] = NONE;
+  Sprite s = sprites[x][y];
+  spriteSetNoAnimation(s);
+  spriteSetRotation(s, 0);
+}
+
+
+void moveSnake() {
+  int x = snake[0][0] + dx;
+  int y = snake[0][1] + dy;
+  int cell = grid[x][y];
+
+  if (cell == WALL) {
+    if (!optStop) mode = GAMEOVER;
+    dx = snake[0][0] - snake[1][0];
+    dy = snake[0][1] - snake[1][1];
+    return;
+  }
+
+  if (cell == SNAKE) {
+    if (!optCut) { mode = GAMEOVER; return; }
+    int prevLen = len;
+    for(int i = 0; i < len; ++i)
+      if (snake[i][0] == x && snake[i][1] == y)
+        { len=i; setSnakeTile(len-1); break; }
+    for(int i = len; i < prevLen; ++i)
+      resetTile(snake[i][0], snake[i][1]);
+    soundPlay(cut, FALSE);
+  }
+
+  if (cell != NONE && cell != FOOD && cell != SNAKE)
+    { mode = GAMEOVER; return; }
+
+  for(int i = len; i > 0; --i) {
+    snake[i][0] = snake[i-1][0];
+    snake[i][1] = snake[i-1][1];
+  }
+  snake[0][0] = x;
+  snake[0][1] = y;
+
+  if (cell == FOOD || cell == SNAKE) {
+    grid[x][y] = SNAKE;
+    ++len;
+    setSnakeTile(0);
+    setSnakeTile(1);
+  } else {
+    grid[x][y] = SNAKE;
+    setSnakeTile(0);
+    setSnakeTile(1);
+    setSnakeTile(len-1);
+    resetTile(snake[len][0], snake[len][1]);
+  }
+
+  if (cell == FOOD) {
+    --foodCount;
+    soundPlay(nom, FALSE);
+    putFood();
+  }
+}
+
+
+void restart() {
+  optStepFrames = 12;
+  optWalls = 0;
+  optCut = TRUE;
+  optStop = TRUE;
+  switch(difficulty) {
+  case 2:
+    optStepFrames = 12;
+    optWalls = 0.01;
+    break;
+  case 3:
+    optStepFrames = 10;
+    optWalls = 0.02;
+    optCut = FALSE;
+    break;
+  case 4:
+    optStepFrames = 8;
+    optWalls = 0.04;
+    optCut = FALSE;
+    optStop = FALSE;
+    break;
+  case 5:
+    optStepFrames = 6;
+    optWalls = 0.06;
+    optCut = FALSE;
+    optStop = FALSE;
+    break;
+  };
+
+
+  for(int x = 0; x < WIDTH; ++x) {
+    for(int y = 0; y < HEIGHT; ++y) {
+      grid[x][y] = NONE;
+      Sprite s = sprites[x][y];
+      spriteSetRotation(s, 0);
+      spriteSetNoAnimation(s);
+      if ( x == 0 || y == 0
+        || x == WIDTH-1 || y == HEIGHT-1
+        || randomFloat() < optWalls )
+      {
+        grid[x][y] = WALL;
+        spriteSetAnimation(s, tileWall);
+      }
+    }
+  }
+
+  len = 2;
+  snake[0][0] = WIDTH/2;
+  snake[0][1] = HEIGHT/2;
+  snake[1][0] = snake[0][0] - 1;
+  snake[1][1] = snake[0][1];
+  for(int i = 0; i < len; ++i)
+    grid[ snake[i][0] ][ snake[i][1] ] = SNAKE;
+  for(int i = 0; i < len; ++i)
+    setSnakeTile(i);
+
+  dx = 1;
+  dy = 0;
+
+  foodCount = 0;
+  putFood();
+
+  framesRemain = optStepFrames;
+  mode = START;
+}
+
+
+void init() {
+  worldSetWidth(SIZE * WIDTH);
+  worldSetHeight(SIZE * HEIGHT);
+  worldSetFrameRate(FRAMERATE);
+
+  storage = createGroup();
+  char *tiles[6] = { tileWall, tileFood, tileHead, tileBody, tileBend, tileTail };
+  for(int i = 0; i < 6; ++i) {
+    Sprite s = createSprite(0, 0);
+    spriteSetAnimation(s, tiles[i]);
+    groupAdd(storage, s);
+  }
+  groupSetVisibleEach(storage, FALSE);
+
+  gridGroup = createGroup();
+  for(int x = 0; x < WIDTH; ++x) {
+    for(int y = 0; y < HEIGHT; ++y) {
+      Sprite s = createSpriteEx((x + 0.5)*SIZE, (y + 0.5)*SIZE, SIZE, SIZE);
+      spriteSetShapeColor(s, "transparent");
+      groupAdd(gridGroup, s);
+      sprites[x][y] = s;
+    }
+  }
+
+  nom = createSound("data/sound/beep.ogg");
+  cut = createSound("data/sound/nom.ogg");
+
+  mode = MENU;
+}
+
+
+void draw() {
+  double cx = worldGetWidth()/2.0;
+  double cy = worldGetHeight()/2.0;
+
+  if (mode == PLAY) {
+    int prevdx = snake[0][0] - snake[1][0];
+    int prevdy = snake[0][1] - snake[1][1];
+
+    if      (keyWentDown("left")  && (prevdx !=  1 || prevdy !=  0)) { dx = -1; dy =  0; framesRemain = 0; }
+    else if (keyWentDown("right") && (prevdx != -1 || prevdy !=  0)) { dx =  1; dy =  0; framesRemain = 0; }
+    else if (keyWentDown("up")    && (prevdx !=  0 || prevdy !=  1)) { dx =  0; dy = -1; framesRemain = 0; }
+    else if (keyWentDown("down")  && (prevdx !=  0 || prevdy != -1)) { dx =  0; dy =  1; framesRemain = 0; }
+    if (--framesRemain <= 0) {
+      moveSnake();
+      framesRemain = optStepFrames;
+    }
+    drawSprites();
+    if (keyWentDown("escape")) mode = MENU;
+  } else
+  if (mode == GAMEOVER) {
+    drawSprites();
+
+    fill(rgba(1, 1, 1, 0.25));
+    rect(0, 0, worldGetWidth(), worldGetHeight());
+
+    textAlign(HALIGN_CENTER, HALIGN_CENTER);
+    textSize(24);
+    text("Game Over", cx, cy);
+    textSize(12);
+    text("press enter", cx, cy + 24);
+    if (keyWentDown("return")) mode = MENU;
+    if (keyWentDown("escape")) mode = MENU;
+  } else
+  if (mode == START) {
+    drawSprites();
+
+    fill(rgba(1, 1, 1, 0.25));
+    rect(0, 0, worldGetWidth(), worldGetHeight());
+
+    textAlign(HALIGN_CENTER, VALIGN_CENTER);
+    textSize(24);
+    text("Press enter to start", cx, cy);
+    if (keyWentDown("return")) mode = PLAY;
+    if (keyWentDown("escape")) mode = MENU;
+  } else
+  if (mode == MENU) {
+    textAlign(HALIGN_LEFT, VALIGN_CENTER);
+    textSize(24);
+    int x = cx - 100;
+    int y = cy - 24*3;
+    text("Select difficulty level:", x, y); y += 24;
+    text("1 - very easy",            x, y); y += 24;
+    text("2 - easy",                 x, y); y += 24;
+    text("3 - medium",               x, y); y += 24;
+    text("4 - hard",                 x, y); y += 24;
+    text("5 - very hard",            x, y); y += 24;
+    difficulty = 0;
+    if (keyWentDown("1")) difficulty = 1;
+    if (keyWentDown("2")) difficulty = 2;
+    if (keyWentDown("3")) difficulty = 3;
+    if (keyWentDown("4")) difficulty = 4;
+    if (keyWentDown("5")) difficulty = 5;
+    if (difficulty) restart();
+    if (keyWentDown("escape")) worldStop();
+  }
+}
+
+int main() {
+	worldSetInit(&init);
+	worldSetDraw(&draw);
+	worldRun();
+	return 0;
+}