diff --git a/js/webtris.js b/js/webtris.js
index 778ac37..ed9beef 100644
--- a/js/webtris.js
+++ b/js/webtris.js
@@ -1,23 +1,23 @@
-Array.prototype.add = function(movement) {
-    return this.map((x, i) => x + movement[i])
+Array.prototype.add = function(other) {
+    return this.map((x, i) => x + other[i])
 }
-
 Array.prototype.rotate = function(spin) {
     return [spin*pos[1], pos[0]]
 }
+Array.prototype.sample = function() {
+    return this.splice(Math.floor(Math.random()*this.length), 1)[0]
+}
 
 
 const MINO_SIZE = 20
-const LINES = 20
-const COLLUMNS = 10
-const INIT_POSITION = [4, -1]
-const ACTION_KEY = {
-    MOVE_LEFT: "ArrowLeft",
-    MOVE_RIGHT: "ArrowRight",
-    SOFT_DROP: "ArrowDown",
-    ROTATE_CW: "ArrowUp",
-    ROTATE_CCW: "z"
-}
+const MATRIX_LINES = 20
+const MATRIX_COLLUMNS = 10
+const NEXT_PIECES = 5
+const INIT_POSITION = [4, 0]
+const LOCK_DELAY = 500
+const FALL_DELAY = 1000
+const AUTOREPEAT_DELAY = 300
+const AUTOREPEAT_PERIOD = 10
 const MOVEMENT = {
     LEFT: [-1, 0],
     RIGHT: [1, 0],
@@ -27,8 +27,27 @@ const SPIN = {
     CW: -1,
     CCW: 1
 }
+const T_SPIN = {
+    NULL: "",
+    MINI: "MINI\nT-SPIN",
+    T_SPIN: "T-SPIN"
+}
+const T_SLOT = {
+    A: 0,
+    B: 1,
+    C: 3,
+    D: 2
+}
+const SCORES = [
+    {LINES_CLEAR_NAME: "", NO_T_SPIN: 0, MINI_T_SPIN: 1, T_SPIN: 4},
+    {LINES_CLEAR_NAME: "SINGLE", NO_T_SPIN: 1, MINI_T_SPIN: 2, T_SPIN: 8},
+    {LINES_CLEAR_NAME: "DOUBLE", NO_T_SPIN: 3, T_SPIN: 12},
+    {LINES_CLEAR_NAME: "TRIPLE", NO_T_SPIN: 5, T_SPIN: 16},
+    {LINES_CLEAR_NAME: "TETRIS", NO_T_SPIN: 8},
+]
 
 
+shapes = []
 class Tetromino {
     constructor() {
         this.pos = INIT_POSITION
@@ -36,20 +55,71 @@ class Tetromino {
         this.rotated_last = false
         this.rotation_point_5_used = false
         this.hold_enabled = true
-        this.SRS = {
-            "-1": [
-                [[0, 0], [-1, 0], [-1, -1], [0, 2], [-1, 2]],
-                [[0, 0], [1, 0], [1, 1], [0, -2], [1, -2]],
-                [[0, 0], [1, 0], [1, -1], [0, 2], [1, 2]],
-                [[0, 0], [-1, 0], [-1, 1], [0, 2], [-1, -2]],
+        this.srs = {
+            CW: [
+                [[0, 0], [-1, 0], [-1, -1], [0,  2], [-1,  2]],
+                [[0, 0], [ 1, 0], [ 1,  1], [0, -2], [ 1, -2]],
+                [[0, 0], [ 1, 0], [ 1, -1], [0,  2], [ 1,  2]],
+                [[0, 0], [-1, 0], [-1,  1], [0,  2], [-1, -2]],
             ],
-            1: [
-                [[0, 0], [1, 0], [1, -1], [0, 2], [1, 2]],
-                [[0, 0], [1, 0], [1, 1], [0, -2], [1, -2]],
-                [[0, 0], [-1, 0], [-1, -1], [0, 2], [-1, 2]],
-                [[0, 0], [-1, 0], [-1, 1], [0, -2], [-1, -2]],
+            CCW: [
+                [[0, 0], [ 1, 0], [ 1, -1], [0,  2], [ 1,  2]],
+                [[0, 0], [ 1, 0], [ 1,  1], [0, -2], [ 1, -2]],
+                [[0, 0], [-1, 0], [-1, -1], [0,  2], [-1,  2]],
+                [[0, 0], [-1, 0], [-1,  1], [0, -2], [-1, -2]],
             ],
         }
+        if (!shapes.lenght)
+            shapes = ['I', 'J', 'L', 'O', 'S', 'T', 'Z']
+        this.shape = shapes.sample()
+        switch(this.shape) {
+            case 'I':
+                this.color = "cyan"
+                this.minoes_pos = [[-1, 0], [0, 0], [1, 0], [2, 0]]
+                this.srs = {
+                    CW: [
+                        [[ 1,  0], [-1,  0], [ 2,  0], [-1,  1], [ 2, -2]],
+                        [[ 0,  1], [-1,  1], [ 2,  1], [-1, -1], [ 2,  2]],
+                        [[-1,  0], [ 1,  0], [-2,  0], [ 1, -1], [-2,  2]],
+                        [[ 0,  1], [ 1, -1], [-2, -1], [ 1,  1], [-2, -2]],
+                    ],
+                    CCW: [
+                        [[ 0,  1], [-1,  1], [ 2,  1], [-1, -1], [ 2,  2]],
+                        [[-1,  0], [ 1,  0], [-2,  0], [ 1, -1], [-2,  2]],
+                        [[ 0, -1], [ 1, -1], [-2, -1], [ 1,  1], [-2, -2]],
+                        [[ 1,  0], [-1,  0], [ 2,  0], [-1,  1], [ 2, -2]],
+                    ],
+                }
+                break
+            case 'J':
+                this.color = "blue"
+                this.minoes_pos = [[-1, -1], [-1, 0], [0, 0], [1, 0]]
+                break
+            case 'L':
+                this.color = "orange"
+                this.minoes_pos = [[-1, 0], [0, 0], [1, 0], [1, -1]]
+                break
+            case 'O':
+                this.color = "yellow"
+                this.minoes_pos = [[0, 0], [1, 0], [0, -1], [1, -1]]
+                this.srs = {
+                    CW: [[]],
+                    CCW: [[]]
+                }
+                break
+            case 'S':
+                this.color = "green"
+                this.minoes_pos = [[-1, -1], [0, -1], [0, 0], [1, 0]]
+                break
+            case 'T':
+                this.color = "magenta"
+                this.minoes_pos = [[-1, 0], [0, 0], [1, 0], [0, -1]]
+                break
+            case 'Z':
+                this.color = "red"
+                this.minoes_pos = [[-1, -1], [0, -1], [0, 0], [1, 0]]
+                break
+        }
     }
         
     get abs_minoes_pos() {
@@ -61,15 +131,6 @@ class Tetromino {
     }
 }
 
-
-class T_Tetrimino extends Tetromino {
-    constructor() {
-        super()
-        this.color = "magenta"
-        this.minoes_pos = [[-1, 0], [0, 0], [1, 0], [0, 1]]
-    }
-}
-
     
 function draw_mino(context, x, y, color) {
     context.fillStyle = color
@@ -80,11 +141,11 @@ function draw_mino(context, x, y, color) {
 
 class Matrix {
     constructor() {
-        this.cells = Array.from(Array(COLLUMNS), y => Array(LINES))
+        this.cells = Array.from(Array(MATRIX_COLLUMNS), y => Array(MATRIX_LINES))
     }
     
     cell_is_occupied(x, y) {
-        return 0 <= x && x < COLLUMNS && y < LINES ? this.cells[x][y] : true
+        return 0 <= x && x < MATRIX_COLLUMNS && y < MATRIX_LINES ? this.cells[x][y] : true
     }
     
     space_to_move(piece_pos, minoes_pos) {
@@ -99,11 +160,11 @@ class Matrix {
         // grid
         context.strokeStyle = "rgba(128, 128, 128, 128)";
         context.beginPath();
-        for (var x = 0; x <= COLLUMNS*MINO_SIZE; x += MINO_SIZE) {
+        for (var x = 0; x <= MATRIX_COLLUMNS*MINO_SIZE; x += MINO_SIZE) {
             context.moveTo(x, 0);
             context.lineTo(x, matrixCanvas.height);
         }
-        for (var y = 0; y <= LINES*MINO_SIZE; y += MINO_SIZE) {
+        for (var y = 0; y <= MATRIX_LINES*MINO_SIZE; y += MINO_SIZE) {
             context.moveTo(0, y);
             context.lineTo(matrixCanvas.width, y);
         }
@@ -116,18 +177,22 @@ function move(movement) {
     const test_pos = tetro.pos.add(movement)
     if (matrix.space_to_move(test_pos, tetro.minoes_pos)) {
         tetro.pos = test_pos
+        return true
+    }
+    else {
+        return false
     }
 }
 
 function rotate(spin) {
     const text_minoes_pos = tetro.minoes_pos.map(pos => [spin*pos[1], pos[0]])
     rotation_point = 0
-    for (const movement of tetro.SRS[spin][tetro.orientation]) {
+    for (const movement of tetro.srs[spin==SPIN.CW?"CW":"CCW"][tetro.orientation]) {
         const test_pos = [tetro.pos[0]+movement[0], tetro.pos[1]+movement[1]]
         if (matrix.space_to_move(test_pos, text_minoes_pos)) {
             tetro.pos = test_pos
             tetro.minoes_pos = text_minoes_pos
-            tetro.orientation = (tetro.orientation + spin + 4) % 4
+            tetro.orientation = (tetro.orientation - spin + 4) % 4
             break;
         }
         rotation_point++
@@ -138,46 +203,120 @@ function fall() {
     move(MOVEMENT.DOWN);
 }
 
+function moveLeft() {
+    move(MOVEMENT.LEFT);
+}
+
+function moveRight() {
+    move(MOVEMENT.RIGHT)
+}
+
+function softDrop() {
+    move(MOVEMENT.DOWN)
+}
+
+function hardDrop() {
+    while(move(MOVEMENT.DOWN)) {
+
+    }
+}
+
+function rotateCW() {
+    rotate(SPIN.CW)
+}
+
+function rotateCCW() {
+    rotate(SPIN.CCW)
+}
+
+actions = {
+    "ArrowLeft":    moveLeft,
+    "ArrowRight":   moveRight,
+    "ArrowDown":    softDrop,
+    " ":            hardDrop,
+    "ArrowUp":      rotateCW,
+    "z":            rotateCCW
+}
+
+pressedKeys = new Set()
+repeatableActions = [moveLeft, moveRight, softDrop]
+actionsToRepeat = []
+autorepeatTimeoutID = null
+autorepeatIntervalID = null
+
+function autorepeat() {
+    if (actionsToRepeat.length) {
+        actionsToRepeat[0]()
+        if (autorepeatTimeoutID) {
+            autorepeatTimeoutID = clearTimeout(autorepeatTimeoutID)
+            autorepeatIntervalID = setInterval(autorepeat, AUTOREPEAT_PERIOD)
+        }
+    }
+    else {
+        if (autorepeatTimeoutID)
+            autorepeatTimeoutID = clearTimeout(autorepeatTimeoutID)
+        if (autorepeatIntervalID)
+            autorepeatIntervalID = clearInterval(autorepeatIntervalID)
+    }
+}
+
 function keyDownHandler(e) {
-    switch(e.key){
-        case ACTION_KEY.MOVE_LEFT:
-            move(MOVEMENT.LEFT);
-            break
-        case ACTION_KEY.MOVE_RIGHT:
-            move(MOVEMENT.RIGHT);
-            break
-        case ACTION_KEY.SOFT_DROP:
-            move(MOVEMENT.DOWN);
-            break
-        case ACTION_KEY.ROTATE_CW:
-            rotate(SPIN.CW);
-            break
-        case ACTION_KEY.ROTATE_CCW:
-            rotate(SPIN.CCW);
-            break
+    if (!pressedKeys.has(e.key)) {
+        pressedKeys.add(e.key)
+        if (e.key in actions) {
+            action = actions[e.key]
+            action()
+            if (repeatableActions.includes(action)) {
+                actionsToRepeat.unshift(action)
+                if (autorepeatTimeoutID) {
+                    autorepeatTimeoutID = clearTimeout(autorepeatTimeoutID)
+                }
+                if (autorepeatIntervalID) {
+                    autorepeatIntervalID = clearInterval(autorepeatIntervalID)
+                }
+                if (actionsToRepeat == softDrop)
+                    autorepeatIntervalID = setInterval(autorepeat, FALL_DELAY / 20)
+                else
+                    autorepeatTimeoutID = setTimeout(autorepeat, AUTOREPEAT_DELAY)
+            }
+        }
     }
 }
 
 function keyUpHandler(e) {
+    pressedKeys.delete(e.key)
+    if (e.key in actions) {
+        action = actions[e.key]
+        if (actionsToRepeat.includes(action)) {
+            actionsToRepeat.splice(actionsToRepeat.indexOf(action), 1)
+            if (!actionsToRepeat.length) {
+                if (autorepeatTimeoutID) {
+                    autorepeatTimeoutID = clearTimeout(autorepeatTimeoutID)
+                }
+                if (autorepeatIntervalID) {
+                    autorepeatIntervalID = clearInterval(autorepeatIntervalID)
+                }
+            }
+        }
+    }
 }
 
 function draw() {
-    matrixContext.clearRect(0, 0, COLLUMNS*MINO_SIZE, LINES*MINO_SIZE);
+    matrixContext.clearRect(0, 0, MATRIX_COLLUMNS*MINO_SIZE, MATRIX_LINES*MINO_SIZE);
     matrix.draw(matrixContext)
     tetro.draw(matrixContext)
     requestAnimationFrame(draw)
 }
 
 window.onload = function() {
-
     matrixCanvas = document.getElementById("matrix");
     matrixContext = matrixCanvas.getContext("2d");
 
-    tetro = new T_Tetrimino()
     matrix = new Matrix()
+    tetro = new Tetromino()
 
     document.addEventListener("keydown", keyDownHandler, false);
     document.addEventListener("keyup", keyUpHandler, false);
-    setInterval(fall, 1000);
+    setInterval(fall, FALL_DELAY);
     requestAnimationFrame(draw)
 }
\ No newline at end of file