Поставьте код
var head = {
x: px,
y: py,
};
snake.unshift(head);
после
if (px >= canvas.width) {
px = 0;
}
if (px + box < 0) {
px = canvas.width;
}
if (py >= canvas.height) {
py = 0;
}
if (py + box < 0) {
py = canvas.height;
}
Потому-что у вас всё по китайски, вы сначала делаете движение,потом добавляете, а потом уже делаете проверки. А нужно все наоборот.
Использование setInterval для gameLoop - зло, никогда так не делайте. Используйте requestAnimationFrame или setTimeout на худой конец.
И вообще структура ужасная не читабельно. Разделите отдельно фукнция просчет физики, отдельно рендеринг не смешивайте и вызывайте в gameloop по порядку всё.