diff --git a/island.ts b/island.ts
index d278235..d7a3b95 100644
--- a/island.ts
+++ b/island.ts
@@ -5,6 +5,8 @@ const BLOWUP = 4;
 const WIDTH = 240;
 const HEIGHT = 135;
 
+const DEFAULT_SEED = 128;
+
 type Lookup2d = (x: number, y: number) => number;
 function dim(width: number, height: number): Lookup2d {
   return function xy(x: number, y: number) {
@@ -21,10 +23,26 @@ class IslandGrid {
 
   xy: Lookup2d;
 
+  basePos: number;
+  lobePos1: number;
+  lobePos2: number;
+
+  done = false;
+
   constructor(public width: number, public height: number, seed: number) {
     this.data = Array(width * height).fill(0);
     this.rng = mulberry32(seed);
     this.xy = dim(width, height);
+
+    this.basePos = this.data.length >> 1;
+    this.lobePos1 = this.xy(
+      (WIDTH >> 1) + (this.rng() % 48) - 24,
+      (HEIGHT >> 1) + (this.rng() % 48) - 24
+    );
+    this.lobePos2 = this.xy(
+      (WIDTH >> 1) + (this.rng() % 48) - 24,
+      (HEIGHT >> 1) + (this.rng() % 48) - 24
+    );
   }
 
   public get(x: number, y: number): number {
@@ -67,6 +85,80 @@ class IslandGrid {
 
     return hitPositions;
   }
+
+  public drop(pos: number): void {
+    const lowerNeighbors: number[] = [];
+
+    const check = (adjPos: number) => {
+      if (this.data[adjPos] < this.data[pos]) {
+        lowerNeighbors.push(adjPos);
+      }
+    };
+
+    // try to roll in cardinal directions
+
+    check((pos - this.width) % this.data.length);
+    check((pos - 1) % this.data.length);
+    check((pos + 1) % this.data.length);
+    check((pos + this.width) % this.data.length);
+
+    if (lowerNeighbors.length > 0) {
+      const downhill = lowerNeighbors[this.rng() % lowerNeighbors.length];
+      return this.drop(downhill);
+    }
+
+    // try to roll in diagonal directions
+
+    check((pos - this.width - 1) % this.data.length);
+    check((pos - this.width + 1) % this.data.length);
+    check((pos + this.width - 1) % this.data.length);
+    check((pos + this.width + 1) % this.data.length);
+
+    if (lowerNeighbors.length > 0) {
+      const downhill = lowerNeighbors[this.rng() % lowerNeighbors.length];
+      return this.drop(downhill);
+    }
+
+    // flat, increase elevation
+    const newValue = ++this.data[pos];
+    if (newValue == 9) {
+      this.done = true;
+    }
+  }
+
+  public dropWithin(tiles: number[]) {
+    if (tiles.length > 0) {
+      this.drop(tiles[this.rng() % tiles.length]);
+    }
+  }
+
+  public step() {
+    const lowlandTiles1 = this.floodSearch(this.lobePos1, (tile) => tile > 0);
+    const lowlandTiles2 = this.floodSearch(this.lobePos2, (tile) => tile > 0);
+
+    // grow shore
+    const shoreTiles1 = lowlandTiles1.filter((pos) => this.data[pos] == 0);
+    this.dropWithin(shoreTiles1);
+    const shoreTiles2 = lowlandTiles2.filter((pos) => this.data[pos] == 0);
+    this.dropWithin(shoreTiles2);
+
+    // seed beach
+    const beachTiles = lowlandTiles1.filter((pos) => this.data[pos] == 1);
+    this.dropWithin(beachTiles);
+
+    // expand forest
+    const forestTiles = lowlandTiles1.filter((pos) => this.data[pos] == 2);
+    this.dropWithin(forestTiles);
+    this.dropWithin(forestTiles);
+    this.dropWithin(forestTiles);
+
+    // form mountain
+    const mountainTiles = this.floodSearch(this.basePos, (tile) => tile > 4);
+    this.dropWithin(mountainTiles); // GENERATOR
+
+    // const erodePos = islandTiles[islands.rng() % islandTiles.length];
+    // islands.data[erodePos] = Math.max(islands.data[erodePos] - 1, 0);
+  }
 }
 
 function renderIslands(islands: IslandGrid, cx: CanvasRenderingContext2D) {
@@ -109,103 +201,13 @@ export function IslandApplet() {
   });
   cx.scale(BLOWUP, BLOWUP);
 
-  const seedInput = h("input", { type: "number", valueAsNumber: 128 });
+  const seedInput = h("input", { type: "number", valueAsNumber: DEFAULT_SEED });
   const seedLabel = h("label", {}, "Seed:", seedInput);
 
   // STATE
 
   let timerId: number;
-  let islands = new IslandGrid(WIDTH, HEIGHT, 128);
-
-  // GENERATOR
-
-  const len = islands.data.length;
-  const basePos = len >> 1;
-  const lobePos1 = islands.xy(
-    (WIDTH >> 1) + (islands.rng() % 48) - 24,
-    (HEIGHT >> 1) + (islands.rng() % 48) - 24
-  );
-  const lobePos2 = islands.xy(
-    (WIDTH >> 1) + (islands.rng() % 48) - 24,
-    (HEIGHT >> 1) + (islands.rng() % 48) - 24
-  );
-  const width = islands.width;
-
-  function drop(pos: number) {
-    const lowerNeighbors: number[] = [];
-
-    function check(adjPos: number) {
-      if (islands.data[adjPos] < islands.data[pos]) {
-        lowerNeighbors.push(adjPos);
-      }
-    }
-
-    // try to roll in cardinal directions
-
-    check((pos - width) % len);
-    check((pos - 1) % len);
-    check((pos + 1) % len);
-    check((pos + width) % len);
-
-    if (lowerNeighbors.length > 0) {
-      const downhill = lowerNeighbors[islands.rng() % lowerNeighbors.length];
-      return drop(downhill);
-    }
-
-    // try to roll in diagonal directions
-
-    check((pos - width - 1) % len);
-    check((pos - width + 1) % len);
-    check((pos + width - 1) % len);
-    check((pos + width + 1) % len);
-
-    if (lowerNeighbors.length > 0) {
-      const downhill = lowerNeighbors[islands.rng() % lowerNeighbors.length];
-      return drop(downhill);
-    }
-
-    // flat, increase elevation
-    const newValue = ++islands.data[pos];
-    if (newValue == 9) {
-      clearInterval(timerId);
-    }
-  }
-
-  function dropWithin(tiles: number[]) {
-    if (tiles.length > 0) {
-      drop(tiles[islands.rng() % tiles.length]);
-    }
-  }
-
-  function tick() {
-    const lowlandTiles1 = islands.floodSearch(lobePos1, (tile) => tile > 0);
-    const lowlandTiles2 = islands.floodSearch(lobePos2, (tile) => tile > 0);
-
-    // grow shore
-    const shoreTiles1 = lowlandTiles1.filter((pos) => islands.data[pos] == 0);
-    dropWithin(shoreTiles1);
-    const shoreTiles2 = lowlandTiles2.filter((pos) => islands.data[pos] == 0);
-    dropWithin(shoreTiles2);
-
-    // seed beach
-    const beachTiles = lowlandTiles1.filter((pos) => islands.data[pos] == 1);
-    dropWithin(beachTiles);
-
-    // expand forest
-    const forestTiles = lowlandTiles1.filter((pos) => islands.data[pos] == 2);
-    dropWithin(forestTiles);
-    dropWithin(forestTiles);
-    dropWithin(forestTiles);
-
-    // form mountain
-    const mountainTiles = islands.floodSearch(basePos, (tile) => tile > 4);
-    dropWithin(mountainTiles);
-
-    // const erodePos = islandTiles[islands.rng() % islandTiles.length];
-    // islands.data[erodePos] = Math.max(islands.data[erodePos] - 1, 0);
-
-    renderIslands(islands, cx);
-  }
+  let islands = new IslandGrid(WIDTH, HEIGHT, DEFAULT_SEED);
 
   // CONTROLS
 
@@ -214,9 +216,19 @@ export function IslandApplet() {
     {
       onclick: () => {
         clearInterval(timerId);
-        console.log(seedInput.valueAsNumber);
+
         islands = new IslandGrid(WIDTH, HEIGHT, seedInput.valueAsNumber);
-        timerId = setInterval(tick, 1000 / 30);
+
+        timerId = setInterval(function tick() {
+          islands.step();
+          islands.step();
+          islands.step();
+          if (islands.done) {
+            clearInterval(timerId);
+          }
+
+          renderIslands(islands, cx);
+        }, 1000 / 30);
       },
     },
     "Generate"