diff --git a/backend/src/api.ts b/backend/src/api.ts
index 75dd366c86bcafdf5e96ec482ae177d92f79fa0b..39605ba8eddd7788ccb4c75eaf5698e9a4b8ff64 100644
--- a/backend/src/api.ts
+++ b/backend/src/api.ts
@@ -105,17 +105,11 @@ const apiPlugin: FastifyPluginCallback = (fastify, options, next) => {
       },
     },
     handler: async (request, reply) => {
-      const game = await connection<Types.Game>("games")
+      const games = await connection<Types.Game>("games")
         .where("id", request.params.id)
-        .first();
-      if (game === undefined) {
-        reply.code(404).send();
-      } else {
-        const games = await connection<Types.Game>("games")
-          .update(request.body)
-          .returning("*");
-        reply.code(200).send(games[0]);
-      }
+        .update(request.body)
+        .returning("*");
+      reply.code(200).send(games[0]);
     },
   });
 
@@ -136,7 +130,7 @@ const apiPlugin: FastifyPluginCallback = (fastify, options, next) => {
     handler: async (request, reply) => {
       const clues = await connection
         .table("clues")
-        .insert(request.body)
+        .insert({ game_id: request.params.id, ...request.body })
         .returning<Types.Clue[]>("*");
 
       reply.code(200).send(clues[0]);
@@ -158,17 +152,11 @@ const apiPlugin: FastifyPluginCallback = (fastify, options, next) => {
       },
     },
     handler: async (request, reply) => {
-      const clue = await connection<Types.Clue>("clues")
+      const clues = await connection<Types.Clue>("clues")
         .where("id", request.params.id)
-        .first();
-      if (clue === undefined) {
-        reply.code(404).send();
-      } else {
-        const clues = await connection<Types.Clue>("clues")
-          .update(request.body)
-          .returning("*");
-        reply.code(200).send(clues[0]);
-      }
+        .update(request.body)
+        .returning("*");
+      reply.code(200).send(clues[0]);
     },
   });
 
diff --git a/backend/src/schemas.ts b/backend/src/schemas.ts
index aee0dcb1f14844859864a23795e5ca0683461dc7..64068e7e1db852cc37c1a28cb8e4f21c9ac5e731 100644
--- a/backend/src/schemas.ts
+++ b/backend/src/schemas.ts
@@ -36,6 +36,6 @@ export const Shot = Type.Object({
 });
 
 export const GameUpdate = Type.Omit(Game, ["id"]);
-export const ClueCreate = Type.Pick(Clue, ["game_id", "word_id"]);
+export const ClueCreate = Type.Pick(Clue, ["word_id"]);
 export const ClueUpdate = Type.Pick(Clue, ["began_at", "ended_at"]);
-export const ShotCreate = Type.Omit(Shot, ["id"]);
+export const ShotCreate = Type.Omit(Shot, ["id", "game_id"]);
diff --git a/frontend/src/js/clue.ts b/frontend/src/js/clue.ts
index cdde3cfe69b87ffec8305285a50213ac5dc2e23d..1e29919ed190b1e5f85584a9a18346818df5c01f 100644
--- a/frontend/src/js/clue.ts
+++ b/frontend/src/js/clue.ts
@@ -1,23 +1,18 @@
 import "phaser";
 import FightScene from "./fight_scene";
 
-interface WordObject {
-  id: string;
-  image: string;
-  ocr_confidence: number;
-  ocr_transcript: string;
-}
+import * as Types from "../../../backend/src/types";
 
 class Clue extends Phaser.GameObjects.Sprite {
-  word: WordObject;
+  word: Types.Word;
   scene: FightScene;
 
-  constructor(scene: FightScene, word: WordObject) {
+  constructor(scene: FightScene, word: Types.Word) {
     // TODO: set positions
     super(scene, 400, 300, word.id);
+    scene.add.existing(this);
 
     this.setAlpha(0);
-    scene.add.existing(this);
 
     this.scene = scene;
     this.word = word;
@@ -26,6 +21,7 @@ class Clue extends Phaser.GameObjects.Sprite {
   }
 
   loadTexture() {
+    // this.scene.textures.remove()
     this.scene.textures.addBase64(this.word.id, this.word.image);
     this.scene.textures.once(
       "addtexture",
@@ -45,23 +41,35 @@ class Clue extends Phaser.GameObjects.Sprite {
       5 +
       this.width / 2;
     this.setPosition(x, 50);
+    this.fadeIn();
+  }
+
+  delete() {
+    this.fadeOut(() => {
+      this.scene.textures.remove(this.texture); // TODO
+      this.destroy.bind(this);
+    });
+  }
+
+  fadeIn(onComplete?: Phaser.Types.Tweens.TweenOnCompleteCallback) {
     this.scene.tweens.add({
       targets: this,
       alpha: 1,
       ease: "Linear",
       delay: 0,
       duration: 100,
+      onComplete: onComplete,
     });
   }
 
-  delete() {
+  fadeOut(onComplete?: Phaser.Types.Tweens.TweenOnCompleteCallback) {
     this.scene.tweens.add({
       targets: this,
       alpha: 0,
       ease: "Linear",
       delay: 0,
-      duration: 2000,
-      onComplete: this.destroy.bind(this),
+      duration: 100,
+      onComplete: onComplete,
     });
   }
 }
diff --git a/frontend/src/js/critter.ts b/frontend/src/js/critter.ts
index 5807151e92074621eb96398c3617f58ed6d363a6..b0a64b168e0083a6dd2446a808c92f1a108a2178 100644
--- a/frontend/src/js/critter.ts
+++ b/frontend/src/js/critter.ts
@@ -1,5 +1,4 @@
 import "phaser";
-import Clue from "./clue";
 import FightScene from "./fight_scene";
 
 const SPECIES = ["bear", "wolf", "deer", "boar"];
@@ -15,19 +14,17 @@ enum CritterState {
 
 class Critter extends Phaser.Physics.Arcade.Sprite {
   state: CritterState;
-  clue: Clue;
   scene: FightScene;
   species: string;
   body: Phaser.Physics.Arcade.Body;
 
-  constructor(scene: FightScene, clue: Clue) {
+  constructor(scene: FightScene) {
     const species = SPECIES[Math.floor(Math.random() * SPECIES.length)];
     super(scene, -100, scene.cameras.main.height - 100, species);
     scene.add.existing(this);
 
     this.scene = scene;
     this.species = species;
-    this.clue = clue;
 
     let scale = 2;
     if (this.species === "deer") {
@@ -42,12 +39,7 @@ class Critter extends Phaser.Physics.Arcade.Sprite {
 
     this.setScale(scale);
 
-    this.move();
-
-    this.scene.physics.add.overlap(this.scene.player, this, () => {
-      this.escape();
-      this.clue.delete();
-    });
+    this.walk();
   }
 
   preUpdate(time, delta) {
@@ -68,7 +60,7 @@ class Critter extends Phaser.Physics.Arcade.Sprite {
     return this.x + this.width * 0.5 < 0;
   }
 
-  move() {
+  walk() {
     this.state = CritterState.Moving;
     this.flipX = true;
     this.play({ key: this.species + "_walk", repeat: -1 });
@@ -78,14 +70,14 @@ class Critter extends Phaser.Physics.Arcade.Sprite {
   flee() {
     this.state = CritterState.Fleeing;
     this.flipX = false;
-    this.play(this.species + "_run");
+    this.play({ key: this.species + "_run", repeat: -1 });
     this.body.setVelocity(FLEE_VELOCITY, 0);
   }
 
   escape() {
     this.state = CritterState.Escaping;
     this.flipX = true;
-    this.play(this.species + "_run");
+    this.play({ key: this.species + "_run", repeat: -1 });
     this.body.setVelocity(ESCAPE_VELOCITY, 0);
   }
 }
diff --git a/frontend/src/js/fight_scene.ts b/frontend/src/js/fight_scene.ts
index dd057835ce7f961f610a52d0b732da9de85fa982..87a24b8c53771a1fb207f1cafad91b9d38a9f363 100644
--- a/frontend/src/js/fight_scene.ts
+++ b/frontend/src/js/fight_scene.ts
@@ -165,11 +165,12 @@ export default class FightScene extends Phaser.Scene {
     initAndBindGuessPreview(this);
 
     this.beGame = (await backend.createGame()).data;
-    this.beGame.began_at = new Date().toISOString();
-    await backend.updateGame(this.beGame.id, {
-      began_at: this.beGame.began_at,
-      ended_at: null,
-    });
+    this.beGame = (
+      await backend.updateGame(this.beGame.id, {
+        began_at: new Date().toISOString(),
+        ended_at: null,
+      })
+    ).data;
 
     gameStart(this);
   }
@@ -204,20 +205,6 @@ export default class FightScene extends Phaser.Scene {
     });
   }
 
-  shootSpear(foe: Foe | null, hit: boolean) {
-    const scene = this;
-    if (foe === null || !hit) {
-      this.showMissMessage();
-      new Spear(this, this.player, undefined);
-    } else {
-      this.showHitMessage();
-      // TODO: ew.
-      foe.clue.delete();
-      // scene.foes.splice(scene.foes.indexOf(foe), 1); // FIXME
-      new Spear(this, this.player, foe.critter);
-    }
-  }
-
   initCluesGroup() {
     const bounds = new Phaser.Geom.Rectangle(
       0,
@@ -252,6 +239,23 @@ export default class FightScene extends Phaser.Scene {
     // console.log(similarity, match.beWord.ocr_transcript);
     return result;
   }
+
+  submitTranscription(transcription: string) {
+    const { score, match } = this.findMatchingFoe(transcription);
+    // TODO: visual near misses based on score
+    if (match === null) {
+      // NOOP
+    } else if (score < 0.9) {
+      match.handleFailure();
+      this.showMissMessage();
+      new Spear(this, this.player, undefined);
+    } else {
+      this.foes.splice(this.foes.indexOf(match), 1);
+      match.handleSuccess();
+      this.showHitMessage();
+      new Spear(this, this.player, match.critter);
+    }
+  }
 }
 
 // TODO: remove any
@@ -325,30 +329,25 @@ function initAndBindGuessPreview(scene: FightScene) {
         event.keyCode === Phaser.Input.Keyboard.KeyCodes.ENTER &&
         textEntry.text.length > 0
       ) {
-        submitTranscription(textEntry.text, scene);
+        scene.submitTranscription(textEntry.text);
         textEntry.text = textEntry.text.substr(0, 0);
       }
     },
   );
 }
 
-function submitTranscription(transcription: string, scene: FightScene) {
-  const { score, match } = scene.findMatchingFoe(transcription);
-  scene.shootSpear(match, score >= 0.9);
-}
-
 function gameStart(scene: any) {
   spawn(scene);
 }
 
 async function spawn(scene: any) {
-  await dispatchEnemy(scene);
+  await spawnFoe(scene);
   scene.time.now;
   const delay =
     (8 * 1000 * (60 * 1000 - scene.time.now)) / 60 / 1000 + 2 * 1000;
   setTimeout(() => spawn(scene), Math.max(delay, 2000));
 }
 
-async function dispatchEnemy(scene: any) {
+async function spawnFoe(scene: any) {
   await new Foe(scene).initialize();
 }
diff --git a/frontend/src/js/foe.ts b/frontend/src/js/foe.ts
index 08abc869cd8ebeb1e51e36db5948e1ed58989a3d..c588a637e1ca0a4497e2150511a79b55f3dadc51 100644
--- a/frontend/src/js/foe.ts
+++ b/frontend/src/js/foe.ts
@@ -20,9 +20,48 @@ class Foe {
 
   async initialize() {
     this.beWord = (await backend.getWord()).data;
+    // this.beClue = (
+    //   await backend.createClue(this.scene.beGame.id, {
+    //     word_id: this.beWord.id,
+    //   })
+    // ).data;
+    // this.beClue = (
+    //   await backend.updateClue(this.beClue.id, {
+    //     began_at: new Date().toISOString(),
+    //     ended_at: null,
+    //   })
+    // ).data;
+
     this.clue = new Clue(this.scene, this.beWord);
-    this.critter = new Critter(this.scene, this.clue);
+    this.critter = new Critter(this.scene);
     this.scene.foes.push(this);
+
+    const overlap = this.scene.physics.add.overlap(
+      this.scene.player,
+      this.critter,
+      () => {
+        this.scene.physics.world.removeCollider(overlap);
+        this.critter.escape();
+      },
+    );
+  }
+
+  async handleSuccess() {
+    // TODO: update clue
+    // TODO: post shot
+    // TODO: destroy foe
+    this.clue.delete();
+  }
+
+  async handleFailure() {
+    // TODO: post shot
+    // await backend.createShot(this.scene.beGame.id, {
+    //   clue_id: this.beClue.id,
+    //   began_at: "",
+    //   ended_at: new Date().toISOString(),
+    //   typed: "",
+    //   final: "",
+    // });
   }
 }