diff --git a/frontend/src/js/fight_scene.ts b/frontend/src/js/fight_scene.ts
index 8c1f33fb0f7d2b666415f74bd7d0e9bceb70a1fe..2bb01d0afbaf6afc819aab8dc7abff2a5eae10c8 100644
--- a/frontend/src/js/fight_scene.ts
+++ b/frontend/src/js/fight_scene.ts
@@ -1,17 +1,16 @@
 import "phaser";
 
-import Critter from "./critter";
 import Spear from "./spear";
 import backend from "./backend";
 
 // TODO: write interfaces
 import levenshtein from "damerau-levenshtein";
-import Clue from "./clue";
 
 import * as Types from "../../../backend/src/types";
+import Foe from "./foe";
 
 export default class FightScene extends Phaser.Scene {
-  foes: Array<Critter>;
+  foes: Array<Foe>;
   ground: Phaser.Types.Physics.Arcade.ImageWithStaticBody;
   player: Phaser.Types.Physics.Arcade.SpriteWithDynamicBody;
   cluesGroup: Phaser.Physics.Arcade.Group;
@@ -197,18 +196,18 @@ export default class FightScene extends Phaser.Scene {
     });
   }
 
-  shootSpear(enemy: Critter, hit: boolean) {
+  shootSpear(foe: Foe | null, hit: boolean) {
     const scene = this;
-    if (!hit) {
+    if (foe === null || !hit) {
       this.showMissMessage();
+      new Spear(this, this.player, undefined);
     } else {
       this.showHitMessage();
       // TODO: ew.
-      enemy.clue.delete();
-      scene.foes.splice(scene.foes.indexOf(enemy), 1); // FIXME
+      foe.clue.delete();
+      // scene.foes.splice(scene.foes.indexOf(foe), 1); // FIXME
+      new Spear(this, this.player, foe.critter);
     }
-
-    new Spear(this, this.player, hit ? enemy : undefined);
   }
 
   initCluesGroup() {
@@ -226,6 +225,25 @@ export default class FightScene extends Phaser.Scene {
     });
     this.physics.add.collider(this.cluesGroup, this.cluesGroup);
   }
+
+  findMatchingFoe(transcription: string) {
+    let result: { score: number; match: Foe | null } = {
+      score: -1,
+      match: null,
+    };
+    if (this.foes.length < 1) return result;
+    this.foes.forEach((foe) => {
+      const similarity = levenshtein(
+        transcription.toLowerCase(),
+        foe.beWord.ocr_transcript.toLowerCase(),
+      ).similarity;
+      if (similarity < result.score) return;
+      result = { score: similarity, match: foe };
+    });
+    // match ??= scene.foes[0]; // TODO: remove this
+    // console.log(similarity, match.beWord.ocr_transcript);
+    return result;
+  }
 }
 
 // TODO: remove any
@@ -307,50 +325,22 @@ function initAndBindGuessPreview(scene: FightScene) {
 }
 
 function submitTranscription(transcription: string, scene: FightScene) {
-  if (scene.foes.length < 1) return;
-
-  let similarity = 0;
-  let match = null;
-
-  scene.foes.forEach((critter) => {
-    const s = levenshtein(
-      transcription.toLowerCase(),
-      critter.clue.word.ocr_transcript.toLowerCase(),
-    ).similarity;
-    if (s < similarity) return;
-    similarity = s;
-    match = critter;
-  });
-
-  match ??= scene.foes[0]; // TODO: remove this
-
-  console.log(similarity, match.clue.word.ocr_transcript);
-
-  // TODO: we can have near misses depending on similarity!
-  const hit = similarity >= 0.9;
-  const enemy = match;
-
-  scene.shootSpear(enemy, hit);
+  const { score, match } = scene.findMatchingFoe(transcription);
+  scene.shootSpear(match, score >= 0.9);
 }
 
 function gameStart(scene: any) {
   spawn(scene);
-  // dispatchEnemy(scene);
 }
 
-function spawn(scene: any) {
-  dispatchEnemy(scene);
+async function spawn(scene: any) {
+  await dispatchEnemy(scene);
   scene.time.now;
   const delay =
     (8 * 1000 * (60 * 1000 - scene.time.now)) / 60 / 1000 + 2 * 1000;
   setTimeout(() => spawn(scene), Math.max(delay, 2000));
 }
 
-function dispatchEnemy(scene: any) {
-  backend.getWord().then(function (response) {
-    const clue = new Clue(scene, response.data);
-    const critter = new Critter(scene, clue);
-    // TODO: clue
-    scene.foes.push(critter);
-  });
+async function dispatchEnemy(scene: any) {
+  await new Foe(scene).initialize();
 }
diff --git a/frontend/src/js/foe.ts b/frontend/src/js/foe.ts
new file mode 100644
index 0000000000000000000000000000000000000000..08abc869cd8ebeb1e51e36db5948e1ed58989a3d
--- /dev/null
+++ b/frontend/src/js/foe.ts
@@ -0,0 +1,29 @@
+import "phaser";
+import Clue from "./clue";
+import Critter from "./critter";
+import FightScene from "./fight_scene";
+
+import backend from "./backend";
+import * as Types from "../../../backend/src/types";
+
+class Foe {
+  beWord: Types.Word;
+  beClue: Types.Clue;
+
+  scene: FightScene;
+  critter: Critter;
+  clue: Clue;
+
+  constructor(scene: FightScene) {
+    this.scene = scene;
+  }
+
+  async initialize() {
+    this.beWord = (await backend.getWord()).data;
+    this.clue = new Clue(this.scene, this.beWord);
+    this.critter = new Critter(this.scene, this.clue);
+    this.scene.foes.push(this);
+  }
+}
+
+export default Foe;