     }
 }
 
-bool onDraw()
+static bool onDraw()
 {
     engine.canvas.clear();
     engine.canvas.setMode(0);
-    showGameInfo();
+    if ((engine.canvas.offset.x + engine.NE_TILE_WIDTH - 1 >= game_window.p1.x) &&
+       (engine.canvas.offset.y + engine.NE_TILE_HEIGHT - 1 >= game_window.p1.y) &&
+       (engine.canvas.offset.x <= game_window.p2.x) &&
+       (engine.canvas.offset.y <= game_window.p2.y))
+    {
     engine.worldCoordinates();
-    for (uint8_t row = max(0,(engine.canvas.offset.y >> 3) - 1);
-                 row <= min(6,((engine.canvas.offset.y + engine.NE_TILE_HEIGHT - 1) >> 3) - 1); row++)
-    {
-        for (uint8_t col = (engine.canvas.offset.x >> 3);
-                     col <= min(23, ((engine.canvas.offset.x + engine.NE_TILE_WIDTH - 1) >> 3)); col++)
+    for (uint8_t row = max(0,(engine.canvas.offset.y - game_window.p1.y) >> 3);
+                 row <= min(13,((engine.canvas.offset.y - game_window.p1.y + engine.NE_TILE_HEIGHT - 1) >> 3)); row++)
+    {
+        for (uint8_t col = max(0,(engine.canvas.offset.x - game_window.p1.x)>> 3);
+                     col <= min(23, ((engine.canvas.offset.x - game_window.p1.x + engine.NE_TILE_WIDTH - 1) >> 3)); col++)
         {
-            uint8_t index = (row * 24) + col;
+            uint16_t index = (row * 24) + col;
             uint8_t blockType = gameField[index];
             if (blockType != 0)
             {
                 engine.canvas.setColor(blockColors[blockType - 1]);
-                engine.canvas.drawBitmap1((col<<3), 8 + (row<<3), 8, 8, bgSprites[blockType - 1]);
+                engine.canvas.drawBitmap1(game_window.p1.x + (col<<3), game_window.p1.y + (row<<3),
+                                          8, 8, bgSprites[blockType - 1]);
             }
         }
     }
     engine.canvas.setMode(CANVAS_MODE_TRANSPARENT);
     engine.canvas.setColor(RGB_COLOR8(64,255,255));
     player.draw();
+    engine.localCoordinates();
+    }
+    showGameInfo();
     return true;
 }
 
+static NanoPoint calc_new_screen_position()
+{
+    NanoPoint position = engine.getPosition();
+    if (player.x() - position.x >= game_window.p2.x - 24)
+    {
+        position.x = min(player.x() - (game_window.p2.x - 24), 96);
+    }
+    else if (player.x() - engine.getPosition().x < game_window.p1.x + 24)
+    {
+        position.x = max(0, player.x() - (game_window.p1.x + 24));
+    }
+    if (player.y() - engine.getPosition().y >= 40)
+    {
+        position.y = min(player.y() - 40, 64);
+    }
+    else if (player.y() - engine.getPosition().y < 24)
+    {
+        position.y = max(0, player.y() - 24);
+    }
+    return position;
+}
+
+#ifdef SSD1331_ACCELERATION
+static void moveGameScreen()
+{
+    NanoPoint position = calc_new_screen_position();
+    if (position != engine.getPosition())
+    {
+        NanoPoint delta = position - engine.getPosition();
+        NanoRect block = game_window + delta;
+        block.crop(game_window);
+        // Copy most part of OLED content via hardware accelerator
+        ssd1331_copyBlock(block.p1.x, block.p1.y, block.p2.x, block.p2.y,
+                          block.p1.x - delta.x, block.p1.y - delta.y);
+        engine.moveTo( position );
+        // Now tell the engine to redraw only new areas
+        if ( delta.x > 0)
+            engine.refresh(game_window.p2.x - delta.x, game_window.p1.y, game_window.p2.x, game_window.p2.y);
+        else if ( delta.x < 0 )
+            engine.refresh(game_window.p1.x, game_window.p1.y, game_window.p1.x - delta.x, game_window.p2.y);
+        if ( delta.y > 0)
+            engine.refresh(game_window.p1.x, game_window.p2.y - delta.y, game_window.p2.x, game_window.p2.y);
+        else if ( delta.y < 0 )
+            engine.refresh(game_window.p1.x, game_window.p1.y, game_window.p2.x, game_window.p1.y - delta.y);
+    }
+}
+
+#else // NO SSD1331 Acceleration
+
+static void moveGameScreen()
+{
+    NanoPoint position = calc_new_screen_position();
+    if (position != engine.getPosition())
+    {
+        engine.moveTo( position );
+        engine.refresh( game_window );
+    }
+}
+
+#endif
+
 void movePlayer(uint8_t direction)
 {
     bool animated = false;
-    uint8_t bottomBlock = gameField[blockIdx(player.bottom().x,player.bottom().y)];
-    uint8_t feetBlock = gameField[blockIdx(player.bottom().x,player.bottom().y - 1)];
-    uint8_t handBlock = gameField[blockIdx(player.top().x,player.top().y)];
-    uint8_t centerBlock = gameField[blockIdx(player.center().x,player.center().y)];
-    uint8_t rightBlock = gameField[blockIdx(player.right().x,player.right().y)];
-    uint8_t leftBlock = gameField[blockIdx(player.left().x,player.left().y)];
+    uint8_t bottomBlock = block_at(player.bottom());
+    uint8_t feetBlock = block_at(player.bottom() + (NanoPoint){0,1});
+    uint8_t handBlock = block_at(player.top());
+    uint8_t centerBlock = block_at(player.center());
+    uint8_t rightBlock = block_at(player.right());
+    uint8_t leftBlock = block_at(player.left());
+    moveGameScreen();
     /* If player doesn't stand on the ground, and doesn't hold the pipe,
      * make the player to fall down. */
     if ( !isSolid(bottomBlock) &&

            case BUTTON_RIGHT:
                if (isWalkable(rightBlock))
                {
                    if (player.x() - engine.getPosition().x >= 64)
                    {
                        int16_t newX = min(player.x() - 64, 96);
                        if (newX != engine.getPosition().x)
                        {
                            #ifdef SSD1331_ACCELERATION
                            ssd1331_copyBlock((newX - engine.getPosition().x), 8, 95, 63, 0, 8);
                            engine.moveTo( { newX, engine.getPosition().y } );
                            engine.refresh(95-7, 8, 95, 63);
                            #else
                            engine.moveToAndRefresh( { newX, engine.getPosition().y } );
                            #endif
                        }
                    }
                    player.moveBy( { 1, 0 } );
                    if (isPipe(centerBlock))
                        player.setBitmap( &playerFlyingImage[MAN_ANIM_RIGHT_PIPE][playerAnimation][0] );


void showGameInfo()
{
    engine.canvas.setColor(RGB_COLOR8(0,0,0));
    engine.canvas.fillRect({{0,0},{95,game_window.p1.y - 1}});
    for (uint8_t i=0; i<goldCollection; i++)
    {
        engine.canvas.setColor(RGB_COLOR8(255,255,0));
