Similar to the other state machines, we have 4 states: right, down, left and right for the direction of traversal.

Notice that when going right, we can just take the first element of the array, this can be done by array.shift(), no need to keep track of the columns.

When going down, we take the last element by using array.pop() and incrementing the current row.

When going left, we can again just use array.pop() to get the last element, no need to mess around with the columns.

When going up, we use array.shift() again to get the first element and decrement the current row.

We keep doing this until we have all the numbers.

Note that we're also modifying the original matrix during the traversal so we have to check for empty arrays. We can always make a copy of the array by doing a slice() as to not modify the input params.

```
/**
* @param {number[][]} matrix
* @return {number[]}
*/
var spiralOrder = function(matrix) {
var m = 0;
var state = ['right', 'down', 'left', 'up'];
var results = [];
var curState = 0;
if (matrix.length !== 0) {
var totalNums = (matrix.length * matrix[0].length);
// while there are numbers left, keep printing
while (results.length < totalNums) {
switch (state[curState]) {
case 'right':
if (matrix[m].length !== 0) {
results.push(matrix[m].shift());
} else {
curState = 1;
++m;
}
break;
case 'down':
if (matrix[m] !== undefined && matrix[m].length !== 0) {
results.push(matrix[m].pop());
++m;
} else {
curState = 2;
--m;
}
break;
case 'left':
if (matrix[m].length !== 0) {
results.push(matrix[m].pop());
} else {
curState = 3;
--m;
}
break;
case 'up':
if (matrix[m] !== undefined && matrix[m].length !== 0) {
results.push(matrix[m].shift());
--m;
} else {
curState = 0;
++m;
}
break;
}
}
}
return results;
};
```