Possiamo integrare l'animazione precedente con l'aggiunta dell'interazione con la tastiera, ovvero la possibilità di controllare il movimento di uno sprite attraverso i tasti.
Usa i tasti cursore per muovere il quadrato bianco e i tasti WDSA per muovere il cerchio viola.

Come prima cosa costuisco un oggetto per memorizzare i tasti premuti:
/* gestione eventi da tastiera */
// creo un oggetto che mantiene traccia dei tasti premuti
// e gestisco due set di tasti
// keyboardRight:  W (alto), D (destra), S (basso), A (sinistra)
// keyboardLeft: tasti cursore
function UserInput() {
  this.keyboardRight = {
    up: false,
    right: false,
    down: false,
    left: false
  };
  this.keyboardLeft = {
    up: false,
    right: false,
    down: false,
    left: false
  };
}
//creo l'oggetto
const userInput = new UserInput();
poi, tramite gli eventi di tastiera keydown e keyup, posso settare le variabili corrispondenti ai tasti premuti e gestire meglio la situazione in cui il tasto viene mantenuto premuto:
const handleKeys = function (event) {
  //in base al tipo di evento verifico se il tasto è ancora premuto o meno
  const ok = event.type === 'keydown';
  switch (event.keyCode) {
    // cursor
    case 38:
      userInput.keyboardLeft.up = ok;
      event.preventDefault();
      break;
    case 39:
      userInput.keyboardLeft.right = ok;
      event.preventDefault();
      break;
    case 40:
      userInput.keyboardLeft.down = ok;
      event.preventDefault();
      break;
    case 37:
      userInput.keyboardLeft.left = ok;
      event.preventDefault();
      break;

    // W D S A
    case 87:
      userInput.keyboardRight.up = ok;
      event.preventDefault();
      break;
    case 68:
      userInput.keyboardRight.right = ok;
      event.preventDefault();
      break;
    case 83:
      userInput.keyboardRight.down = ok;
      event.preventDefault();
      break;
    case 65:
      userInput.keyboardRight.left = ok;
      event.preventDefault();
      break;
  }
  //console.log(event.keyCode);
};

//aggancio gli eventi di tastiera 
document.addEventListener('keydown', handleKeys);
document.addEventListener('keyup', handleKeys);
A questo punto devo aggiungere una proprietà agli sprite (animationType) per decidere se deve essere mosso da tastiera e da quale combinazione di tasti:
// gestisco il tipo di animazione
const AnimationType = {
  NONE: 0,
  LINEAR: 1,
  KEYBOARD_RIGHT: 2,
  KEYBOARD_LEFT: 3,
  MOUSE: 4
};

function SpriteRect(x, y, w, h, color, fillColor, deltaX, deltaY) {
  this.type = SpriteType.RECTANGLE;
  this.x = x; // x e y sempre riferiti al centro dell'oggetto
  this.y = y;
  this.w = w;
  this.h = h;
  this.color = color == null ? null : color;
  this.fillColor = fillColor == null ? null : fillColor;
  this.deltaX = deltaX == null ? 0 : deltaX;
  this.deltaY = deltaY == null ? 0 : deltaY;
  //this.animationOn non mi serve più uso animation type
  this.animationType = (this.deltaX !== 0 || this.deltaY !== 0) ? AnimationType.LINEAR : AnimationType.NONE;
  this.alpha = 1;
  this.animationType = AnimationType.LINEAR;
}

function SpriteBackground(x, y, w, h, fillColor) {
  this.type = SpriteType.BACKGROUND;
  this.x = x; // x e y sempre riferiti all'angolo in alto a sinistra
  this.y = y;
  this.w = w;
  this.h = h;
  this.fillColor = fillColor == null ? null : fillColor;
  //this.animationOn non mi serve più uso animation type
  this.animationType = AnimationType.NONE;
  this.alpha = 1;
  //gli oggetti di tipo background sono fissi, non richiedono animationType
}

function SpriteText(x, y, text, font, color, fillColor, textAlign, baseline, deltaX, deltaY) {
  this.type = SpriteType.TEXT;
  this.x = x;
  this.y = y;
  this.text = text;
  this.font = font == null ? '14px Arial' : font;
  this.color = color == null ? null : color;
  this.fillColor = fillColor == null ? null : fillColor;
  this.textAlign = textAlign == null ? 'left' : textAlign;
  this.baseline = baseline == null ? 'bottom' : baseline;
  this.deltaX = deltaX == null ? 0 : deltaX;
  this.deltaY = deltaY == null ? 0 : deltaY;
  //this.animationOn non mi serve più uso animation type
  this.animationType = (this.deltaX !== 0 || this.deltaY !== 0) ? AnimationType.LINEAR : AnimationType.NONE;
  this.alpha = 1;
}
Avendo un nuovo tipo di movimento, quello da tastiera, devo gestire in modo specifico l'aggiornamento della posizione:
function updatePosition() {
  arena.sprites.forEach(sprite => {
    //gestisco lo spostamento da tastiera tramite i tasti cursore
    if (sprite.animationType === AnimationType.KEYBOARD_LEFT || sprite.animationType === AnimationType.KEYBOARD_RIGHT) {
      const keyBoard = sprite.animationType === AnimationType.KEYBOARD_LEFT ? userInput.keyboardLeft : userInput.keyboardRight;
      if (keyBoard.up)
        sprite.y -= Math.abs(sprite.deltaY);
      if (keyBoard.right)
        sprite.x += Math.abs(sprite.deltaX);
      if (keyBoard.down)
        sprite.y += Math.abs(sprite.deltaY);
      if (keyBoard.left)
        sprite.x -= Math.abs(sprite.deltaX);
    } else if (sprite.animationType === AnimationType.LINEAR) {
      //normale animazione
      sprite.x += sprite.deltaX;
      sprite.y += sprite.deltaY;
    }
  });
}
e la collisione con i bordi in quanto, per gli sprite mossi da tastiera, non devo invertire il movimento:
function checkCollision() {
  //verifico la collisione con i bordi
  arena.sprites.forEach(sprite => {
    //memorizzo se lo sprite è mosso dalla tastiera
    if (sprite.animationType === AnimationType.KEYBOARD_LEFT || sprite.animationType === AnimationType.KEYBOARD_RIGHT) {
      switch (sprite.type) {
        case SpriteType.RECTANGLE:
          const w2 = sprite.w / 2;
          const h2 = sprite.h / 2;
          if (sprite.x - w2 < 0 || sprite.x + w2 > innerWidth) {
            //se è mosso dalla tastiera non devo invertire il moto
            sprite.x = sprite.x - w2 < 0 ? w2 : innerWidth - w2;
          }
          if (sprite.y - h2 < 0 || sprite.y + h2 > innerHeight) {
            //se è mosso dalla tastiera non devo invertire il moto
            sprite.y = sprite.y - h2 < 0 ? h2 : innerHeight - h2;
          }
          break;

        case SpriteType.CIRCLE:
          if (sprite.x - sprite.radius < 0 || sprite.x + sprite.radius > innerWidth) {
            sprite.x = sprite.x - sprite.radius < 0 ? sprite.radius : innerWidth - sprite.radius;
          }
          if (sprite.y - sprite.radius < 0 || sprite.y + sprite.radius > innerHeight) {
            sprite.y = sprite.y - sprite.radius < 0 ? sprite.radius : innerHeight - sprite.radius;
          }
          break;
      }

    } else if (sprite.animationType !== AnimationType.NONE) {
      switch (sprite.type) {
        case SpriteType.RECTANGLE:
          const w2 = sprite.w / 2;
          const h2 = sprite.h / 2;
          if (sprite.x - w2 < 0 || sprite.x + w2 > innerWidth) {
            sprite.deltaX = -sprite.deltaX;
            sprite.x += sprite.deltaX;
          }
          if (sprite.y - h2 < 0 || sprite.y + h2 > innerHeight) {
            sprite.deltaY = -sprite.deltaY;
            sprite.y += sprite.deltaY;
          }
          break;

        case SpriteType.CIRCLE:
          if (sprite.x - sprite.radius < 0 || sprite.x + sprite.radius > innerWidth) {
            sprite.deltaX = -sprite.deltaX;
            sprite.x += sprite.deltaX;
          }
          if (sprite.y - sprite.radius < 0 || sprite.y + sprite.radius > innerHeight) {
            sprite.deltaY = -sprite.deltaY;
            sprite.Y += sprite.deltaY;
          }
          break;
      }
    }
  });
}
Ovviamente devo anche aggiungere gli sprite mossi da tastiera:
function createSprites() {
  ...

  //aggiungo un quadrato BIANCO controllato dai tasti cursori
  const velLeft = 3;  // deve essere un numero positivo
  const rectKeyLeft = new SpriteRect(cx + 50, cy, 20, 20, null, '#fff', velLeft, velLeft);
  //cambio il tipo di animazione
  rectKeyLeft.animationType = AnimationType.KEYBOARD_LEFT;
  arena.addSprite(rectKeyLeft);

  //aggiungo un cerchio VIOLA controllato dai tasti WDSA
  const velRight = 3;  // deve essere un numero positivo
  const circleKeyRight = new SpriteCircle(cx - 50, cy, 10, null, '#f0f', velRight, velRight);
  //cambio il tipo di animazione
  circleKeyRight.animationType = AnimationType.KEYBOARD_RIGHT;
  arena.addSprite(circleKeyRight);
}
Il risultato è l'animazione a inizio pagina.