diff --git a/engines/befunge93/README.md b/engines/befunge93/README.md
new file mode 100644
index 0000000..d1c34c6
--- /dev/null
+++ b/engines/befunge93/README.md
@@ -0,0 +1,3 @@
+# Befunge-93
+
+- Interactive input is not supported yet, so currenty division-by-zero throws a runtime error.
diff --git a/engines/befunge93/constants.ts b/engines/befunge93/constants.ts
new file mode 100644
index 0000000..ab53843
--- /dev/null
+++ b/engines/befunge93/constants.ts
@@ -0,0 +1,79 @@
+import { MonacoTokensProvider } from "../types";
+
+export type Bfg93RS = {
+  stack: number[];
+  direction: Bfg93Direction;
+  strMode: boolean;
+};
+
+/** Direction of program counter */
+export enum Bfg93Direction {
+  UP = "up",
+  DOWN = "down",
+  LEFT = "left",
+  RIGHT = "right",
+}
+
+/** Allowed operations in Befunge */
+export enum Bfg93Op {
+  NOOP = " ",
+  ADD = "+",
+  SUBTRACT = "-",
+  MULTIPLY = "*",
+  DIVIDE = "/",
+  MODULO = "%",
+  NOT = "!",
+  GREATER = "`",
+  RIGHT = ">",
+  LEFT = "<",
+  UP = "^",
+  DOWN = "v",
+  RANDOM = "?",
+  H_IF = "_",
+  V_IF = "|",
+  TOGGLE_STR = '"',
+  DUPLICATE = ":",
+  SWAP = "\\",
+  POP_DELETE = "$",
+  POP_OUTINT = ".",
+  POP_OUTCHAR = ",",
+  BRIDGE = "#",
+  GET_DATA = "g",
+  PUT_DATA = "p",
+  STDIN_INT = "&",
+  STDIN_CHAR = "~",
+  END = "@",
+  PUSH_0 = "0",
+  PUSH_1 = "1",
+  PUSH_2 = "2",
+  PUSH_3 = "3",
+  PUSH_4 = "4",
+  PUSH_5 = "5",
+  PUSH_6 = "6",
+  PUSH_7 = "7",
+  PUSH_8 = "8",
+  PUSH_9 = "9",
+}
+
+/** Sample program printing "Hello world" */
+export const sampleProgram = [
+  `"!dlroW ,olleH">:v`,
+  `               |,<`,
+  `               @`,
+].join("\n");
+
+/** Tokens provider */
+export const editorTokensProvider: MonacoTokensProvider = {
+  tokenizer: {
+    root: [
+      [/[\>\^<v\?]/, "red"],
+      [/[\+\-\*\/%!`]/, "orange"],
+      [/[|_]/, "blue"],
+      [/[":\\#]/, "green"],
+      [/[\$\.,]/, "violet"],
+      [/[gp@]/, "indigo"],
+      [/[&~0-9]/, "turquoise"],
+    ],
+  },
+  defaultToken: "comment",
+};
diff --git a/engines/befunge93/engine.ts b/engines/befunge93/engine.ts
new file mode 100644
index 0000000..8c05de3
--- /dev/null
+++ b/engines/befunge93/engine.ts
@@ -0,0 +1,4 @@
+import { setupWorker } from "../setup-worker";
+import Befunge93LanguageEngine from "./runtime";
+
+setupWorker(new Befunge93LanguageEngine());
diff --git a/engines/befunge93/index.ts b/engines/befunge93/index.ts
new file mode 100644
index 0000000..c379859
--- /dev/null
+++ b/engines/befunge93/index.ts
@@ -0,0 +1,11 @@
+import { Renderer } from "./renderer";
+import { LanguageProvider } from "../types";
+import { Bfg93RS, sampleProgram, editorTokensProvider } from "./constants";
+
+const provider: LanguageProvider<Bfg93RS> = {
+  Renderer,
+  sampleProgram,
+  editorTokensProvider,
+};
+
+export default provider;
diff --git a/engines/befunge93/input-stream.ts b/engines/befunge93/input-stream.ts
new file mode 100644
index 0000000..e22b16b
--- /dev/null
+++ b/engines/befunge93/input-stream.ts
@@ -0,0 +1,46 @@
+import { RuntimeError } from "../worker-errors";
+
+/**
+ * A barebones input stream implementation for consuming integers and characters from a string.
+ */
+export default class InputStream {
+  private _text: string;
+
+  /** Create a new input stream loaded with the given input */
+  constructor(text: string) {
+    this._text = text;
+  }
+
+  /** Remove leading whitespace from the current input stream */
+  private exhaustLeadingWhitespace(): void {
+    const firstChar = this._text.trim()[0];
+    const posn = this._text.search(firstChar);
+    this._text = this._text.slice(posn);
+  }
+
+  /** Parse input stream for an integer */
+  getNumber(): number {
+    this.exhaustLeadingWhitespace();
+    // The extra whitespace differentiates whether string is empty or all numbers.
+    if (this._text === "") throw new RuntimeError("Unexpected end of input");
+    let posn = this._text.search(/[^0-9]/);
+    if (posn === 0)
+      throw new RuntimeError(`Unexpected input character: '${this._text[0]}'`);
+    if (posn === -1) posn = this._text.length;
+    // Consume and parse numeric part
+    const numStr = this._text.slice(0, posn);
+    this._text = this._text.slice(posn);
+    return parseInt(numStr, 10);
+  }
+
+  /**
+   * Parse input stream for the first character, and return its ASCII code.
+   * If end of input, returns -1.
+   */
+  getChar(): number {
+    if (this._text.length === 0) return -1;
+    const char = this._text[0];
+    this._text = this._text.slice(1);
+    return char.charCodeAt(0);
+  }
+}
diff --git a/engines/befunge93/renderer.tsx b/engines/befunge93/renderer.tsx
new file mode 100644
index 0000000..14d9ebe
--- /dev/null
+++ b/engines/befunge93/renderer.tsx
@@ -0,0 +1,80 @@
+import { Card, Colors, Icon, IconName } from "@blueprintjs/core";
+import { RendererProps } from "../types";
+import { Bfg93Direction, Bfg93RS } from "./constants";
+
+/** Common border color for dark and light, using transparency */
+export const BorderColor = Colors.GRAY3 + "55";
+
+const styles = {
+  placeholderDiv: {
+    height: "100%",
+    width: "100%",
+    display: "flex",
+    justifyContent: "center",
+    alignItems: "center",
+    fontSize: "1.2em",
+  },
+  rootContainer: {
+    height: "100%",
+    display: "flex",
+    flexDirection: "column" as "column",
+  },
+  dirnContainer: {
+    borderBottom: "1px solid " + BorderColor,
+    padding: "5px 10px",
+  },
+  stackContainer: {
+    padding: 10,
+    height: "100%",
+    display: "flex",
+    flexWrap: "wrap" as "wrap",
+    alignContent: "flex-start",
+    overflowY: "auto" as "auto",
+  },
+  stackItem: {
+    // Sizing
+    width: "10%",
+    height: "40px",
+    margin: "5px 0.25%",
+    // Center-align values
+    display: "flex",
+    justifyContent: "center",
+    alignItems: "center",
+  },
+};
+
+const DirectionIcons: { [k: string]: IconName } = {
+  [Bfg93Direction.RIGHT]: "arrow-right",
+  [Bfg93Direction.LEFT]: "arrow-left",
+  [Bfg93Direction.UP]: "arrow-up",
+  [Bfg93Direction.DOWN]: "arrow-down",
+};
+
+const StackItem = ({ value }: { value: number }) => {
+  const cellStyle = { ...styles.stackItem };
+  return <Card style={cellStyle}>{value}</Card>;
+};
+
+export const Renderer = ({ state }: RendererProps<Bfg93RS>) => {
+  if (state == null)
+    return <div style={styles.placeholderDiv}>Run some code...</div>;
+
+  return (
+    <div style={styles.rootContainer}>
+      <div style={styles.dirnContainer}>
+        <span style={{ fontWeight: "bold", marginRight: 5 }}>Direction: </span>
+        <Icon icon={DirectionIcons[state.direction]} />
+        {/* <span style={{ marginLeft: 10 }} /> */}
+        <span style={{ marginLeft: 30, fontWeight: "bold", marginRight: 5 }}>
+          String mode:{" "}
+        </span>
+        {state.strMode ? "ON" : "OFF"}
+      </div>
+      <div style={styles.stackContainer}>
+        {state.stack.map((value, idx) => (
+          <StackItem key={idx} value={value} />
+        ))}
+      </div>
+    </div>
+  );
+};
diff --git a/engines/befunge93/runtime.ts b/engines/befunge93/runtime.ts
new file mode 100644
index 0000000..6ec9689
--- /dev/null
+++ b/engines/befunge93/runtime.ts
@@ -0,0 +1,407 @@
+import InputStream from "./input-stream";
+import { DocumentRange, LanguageEngine, StepExecutionResult } from "../types";
+import { ParseError, RuntimeError } from "../worker-errors";
+import { Bfg93RS, Bfg93Op, Bfg93Direction } from "./constants";
+
+const ROWSIZE = 80; // Maximum size of a single grid row
+const COLSIZE = 25; // Maximum size of a single grid column
+
+/** Program counter is coordinates in 2D grid. */
+type PC = {
+  x: number; // 0-indexed, goes rightwards
+  y: number; // 0-indexed, goes downwards
+};
+
+/**
+ * Defines bounds of the used portion of the grid. So, if the code
+ * only occupies top-left 30x20 square, all items in array `x` are < 30,
+ * and all items in array `y` are < 20.
+ *
+ * - `bounds.x[10]`: highest index used on 11th row of grid
+ * - `bounds.y[5]`: highest index used on 6th column of grid
+ */
+type CodeBounds = {
+  x: number[];
+  y: number[];
+};
+
+// Default values for internal states
+// Factories are used to create new objects on reset
+const DEFAULT_AST = (): string[] => [];
+const DEFAULT_PC = () => ({ x: -1, y: -1 });
+const DEFAULT_STACK = (): number[] => [];
+const DEFAULT_DIRN = Bfg93Direction.RIGHT;
+const DEFAULT_STR_MODE = false;
+const DEFAULT_BOUNDS = (): CodeBounds => ({
+  x: [],
+  y: [],
+});
+
+// List of characters representing valid Befunge-93 ops
+const OP_CHARS = Object.values(Bfg93Op);
+
+export default class Befunge93LanguageEngine
+  implements LanguageEngine<Bfg93RS>
+{
+  private _ast: string[] = DEFAULT_AST();
+  private _stack: number[] = DEFAULT_STACK();
+  private _pc: PC = DEFAULT_PC();
+  private _dirn: Bfg93Direction = DEFAULT_DIRN;
+  private _strmode: boolean = DEFAULT_STR_MODE;
+  private _bounds: CodeBounds = DEFAULT_BOUNDS();
+  private _input: InputStream = new InputStream("");
+
+  resetState() {
+    this._ast = DEFAULT_AST();
+    this._stack = DEFAULT_STACK();
+    this._pc = DEFAULT_PC();
+    this._dirn = DEFAULT_DIRN;
+    this._strmode = DEFAULT_STR_MODE;
+    this._bounds = DEFAULT_BOUNDS();
+    this._input = new InputStream("");
+  }
+
+  validateCode(code: string) {
+    this.parseCode(code);
+  }
+
+  prepare(code: string, input: string) {
+    this._ast = this.parseCode(code);
+    this._input = new InputStream(input);
+  }
+
+  executeStep(): StepExecutionResult<Bfg93RS> {
+    // Execute and update program counter
+    let output: string | undefined = undefined;
+    let end: boolean = false;
+    if (this._pc.x === -1 && this._pc.y === -1) {
+      this._pc = { x: 0, y: 0 };
+    } else {
+      const result = this.processOp();
+      output = result.output;
+      end = !!result.end;
+    }
+
+    // Prepare location of next step
+    let nextStepLocation: DocumentRange | null = null;
+    if (!end) nextStepLocation = this.toRange(this._pc.y, this._pc.x);
+
+    // Prepare and return execution result
+    const rendererState: Bfg93RS = {
+      stack: this._stack,
+      direction: this._dirn,
+      strMode: this._strmode,
+    };
+    return { rendererState, nextStepLocation, output };
+  }
+
+  private parseCode(code: string) {
+    // A Befunge program can contain any character in the program, so the only
+    // validation to do is ensure program is within 80x25 bounds.
+
+    // Validate that program is within the 80x25 bounds
+    const lines = code.split("\n");
+    if (lines.length > COLSIZE)
+      throw new ParseError(`Code is longer than ${COLSIZE} lines`, {
+        line: COLSIZE,
+      });
+    lines.forEach((line, idx) => {
+      if (line.length > ROWSIZE)
+        throw new ParseError(`Line is longer than ${ROWSIZE} characters`, {
+          line: idx,
+          charRange: { start: ROWSIZE },
+        });
+    });
+
+    // Global bounds for each axis
+    const maxX = Math.max(...lines.map((line) => line.length - 1));
+    const maxY = lines.length - 1;
+
+    // Define bounds for each line and column
+    for (let i = 0; i < COLSIZE; ++i)
+      this._bounds.x[i] = lines[i]?.length - 1 || -1;
+    for (let j = 0; j < ROWSIZE; ++j) this._bounds.y[j] = j <= maxX ? maxY : -1;
+
+    // Pad the program to size 80x25 for execution
+    const grid = lines.map((line) => line.padEnd(80, " "));
+    grid.push(...new Array(25 - lines.length).fill(" ".repeat(80)));
+    return grid;
+  }
+
+  /**
+   * Process the instruction at the current program grid pointer.
+   * Also updates stack and pointer states.
+   * @returns String to append to output, if any
+   */
+  private processOp(): { output?: string; end?: boolean } {
+    const char = this.getGridCell(this._pc.x, this._pc.y);
+    if (this._strmode && char !== '"') {
+      // Push character to string and return;
+      this._stack.push(char.charCodeAt(0));
+      this.updatePointer();
+      return {};
+    }
+
+    let output: string | undefined = undefined;
+    let end: boolean = false;
+
+    const op = this.charToOp(char);
+    if (!op) throw new RuntimeError("Invalid instruction");
+    switch (op) {
+      case Bfg93Op.NOOP: {
+        break;
+      }
+      case Bfg93Op.ADD: {
+        const a = this.popStack();
+        const b = this.popStack();
+        this.pushStack(a + b);
+        break;
+      }
+      case Bfg93Op.SUBTRACT: {
+        const a = this.popStack();
+        const b = this.popStack();
+        this.pushStack(b - a);
+        break;
+      }
+      case Bfg93Op.MULTIPLY: {
+        const a = this.popStack();
+        const b = this.popStack();
+        this.pushStack(a * b);
+        break;
+      }
+      case Bfg93Op.DIVIDE: {
+        const a = this.popStack();
+        const b = this.popStack();
+        if (a === 0) throw new RuntimeError("cannot divide by zero");
+        this.pushStack(Math.floor(b / a));
+        break;
+      }
+      case Bfg93Op.MODULO: {
+        const a = this.popStack();
+        const b = this.popStack();
+        this.pushStack(b % a);
+        break;
+      }
+      case Bfg93Op.NOT: {
+        const val = this.popStack();
+        this.pushStack(val === 0 ? 1 : 0);
+        break;
+      }
+      case Bfg93Op.GREATER: {
+        const a = this.popStack();
+        const b = this.popStack();
+        this.pushStack(b > a ? 1 : 0);
+        break;
+      }
+      case Bfg93Op.RIGHT: {
+        this._dirn = Bfg93Direction.RIGHT;
+        break;
+      }
+      case Bfg93Op.LEFT: {
+        this._dirn = Bfg93Direction.LEFT;
+        break;
+      }
+      case Bfg93Op.UP: {
+        this._dirn = Bfg93Direction.UP;
+        break;
+      }
+      case Bfg93Op.DOWN: {
+        this._dirn = Bfg93Direction.DOWN;
+        break;
+      }
+      case Bfg93Op.RANDOM: {
+        const rand = Math.floor(Math.random() * 4);
+        if (rand === 0) this._dirn = Bfg93Direction.RIGHT;
+        else if (rand === 1) this._dirn = Bfg93Direction.LEFT;
+        else if (rand === 2) this._dirn = Bfg93Direction.UP;
+        else this._dirn = Bfg93Direction.DOWN;
+        break;
+      }
+      case Bfg93Op.H_IF: {
+        const val = this.popStack();
+        if (val === 0) this._dirn = Bfg93Direction.RIGHT;
+        else this._dirn = Bfg93Direction.LEFT;
+        break;
+      }
+      case Bfg93Op.V_IF: {
+        const val = this.popStack();
+        if (val === 0) this._dirn = Bfg93Direction.DOWN;
+        else this._dirn = Bfg93Direction.UP;
+        break;
+      }
+      case Bfg93Op.TOGGLE_STR: {
+        this._strmode = !this._strmode;
+        break;
+      }
+      case Bfg93Op.DUPLICATE: {
+        const val = this.popStack();
+        this.pushStack(val);
+        this.pushStack(val);
+        break;
+      }
+      case Bfg93Op.SWAP: {
+        const top = this.popStack();
+        const other = this.popStack();
+        this.pushStack(top);
+        this.pushStack(other);
+        break;
+      }
+      case Bfg93Op.POP_DELETE: {
+        this.popStack();
+        break;
+      }
+      case Bfg93Op.POP_OUTINT: {
+        const int = this.popStack();
+        output = int.toString() + " ";
+        break;
+      }
+      case Bfg93Op.POP_OUTCHAR: {
+        const charCode = this.popStack();
+        output = String.fromCharCode(charCode);
+        break;
+      }
+      case Bfg93Op.BRIDGE: {
+        this.updatePointer();
+        break;
+      }
+      case Bfg93Op.GET_DATA: {
+        const y = this.popStack();
+        const x = this.popStack();
+        const char = this.getGridCell(x, y);
+        this.pushStack(char.charCodeAt(0));
+        break;
+      }
+      case Bfg93Op.PUT_DATA: {
+        const y = this.popStack();
+        const x = this.popStack();
+        const charCode = this.popStack();
+        this.setGridCell(x, y, charCode);
+        break;
+      }
+      case Bfg93Op.STDIN_INT: {
+        this.pushStack(this._input.getNumber());
+        break;
+      }
+      case Bfg93Op.STDIN_CHAR: {
+        const charCode = this._input.getChar();
+        this.pushStack(charCode);
+        break;
+      }
+      case Bfg93Op.END: {
+        end = true;
+        break;
+      }
+      default: {
+        this.pushStack(parseInt(op, 10));
+        break;
+      }
+    }
+
+    // Update grid pointer and return
+    this.updatePointer();
+    return { output, end };
+  }
+
+  /** Push a number onto the stack */
+  private pushStack(num: number): void {
+    this._stack.push(num);
+  }
+
+  /** Pop a number from stack. If empty stack, returns 0 */
+  private popStack(): number {
+    if (this._stack.length === 0) return 0;
+    else return this._stack.pop()!;
+  }
+
+  /**
+   * Get character at position (x, y) of program grid.
+   * Throws RuntimeError if (x, y) is out of bounds.
+   */
+  private getGridCell(x: number, y: number): string {
+    if (!this.isInGrid(x, y))
+      throw new RuntimeError("Coordinates out of bounds");
+    else return this._ast[y][x];
+  }
+
+  /**
+   * Set cell at (x, y) of program grid to character with given ASCII value.
+   * Throws if (x, y) is out of bounds
+   */
+  private setGridCell(x: number, y: number, asciiVal: number): void {
+    if (!this.isInGrid(x, y))
+      throw new RuntimeError("Coordinates out of bound");
+
+    // Change character at position (x, y)
+    this._ast[y] =
+      this._ast[y].slice(0, x) +
+      String.fromCharCode(asciiVal) +
+      this._ast[y].slice(x + 1);
+
+    // Update grid bounds
+    this._bounds.x[y] = Math.max(this._bounds.x[y], x);
+    this._bounds.y[x] = Math.max(this._bounds.y[x], y);
+  }
+
+  /**
+   * Update program grid pointer according to currently set direction.
+   * Throws RuntimeError if pointer lands outside 80x25 grid.
+   */
+  private updatePointer(): void {
+    // Update pointer
+    if (this._dirn === Bfg93Direction.RIGHT) this._pc.x += 1;
+    else if (this._dirn === Bfg93Direction.LEFT) this._pc.x -= 1;
+    else if (this._dirn === Bfg93Direction.UP) this._pc.y -= 1;
+    else if (this._dirn === Bfg93Direction.DOWN) this._pc.y += 1;
+    else throw new Error("Unknown direction");
+
+    // Check pointer position and wrap if necessary
+    this.wrapPointer();
+  }
+
+  /**
+   * Wraps the pointer around the program bounds. Note that program bounds are
+   * not 80x25 - they are the bounds of the used parts of grid.
+   *
+   * Assumes that only one of x and y-coordinates is out of bounds.
+   */
+  private wrapPointer(): void {
+    if (this._strmode) {
+      // String mode: just wrap the pointer around the 80x25 grid
+      this._pc.x = (this._pc.x + ROWSIZE) % ROWSIZE;
+      this._pc.y = (this._pc.y + COLSIZE) % COLSIZE;
+    } else if (
+      this._dirn === Bfg93Direction.LEFT ||
+      this._dirn === Bfg93Direction.RIGHT
+    ) {
+      // Wrap pointer around code bounds in horizontal direction (along x-axis)
+      if (this._pc.x < 0) this._pc.x = this._bounds.x[this._pc.y];
+      else if (this._pc.x > this._bounds.x[this._pc.y]) this._pc.x = 0;
+    } else {
+      // Wrap pointer around code bounds in vertical direction (along y-axis)
+      if (this._pc.y < 0) this._pc.y = this._bounds.y[this._pc.x];
+      else if (this._pc.y > this._bounds.y[this._pc.x]) this._pc.y = 0;
+    }
+  }
+
+  /**
+   * Cast the given character to corresponding Befunge-93 op.
+   * If character is invalid op, returns null.
+   * @param char Character to cast to Befunge-93 op
+   * @returns Corresponding Befunge-93 op, or null.
+   */
+  private charToOp(char: string): Bfg93Op | null {
+    if (char.length !== 1) throw new Error(`'${char}' not a character`);
+    if (!OP_CHARS.includes(char as Bfg93Op)) return null;
+    else return char as Bfg93Op;
+  }
+
+  /** Convert 2D coordinates to DocumentRange */
+  private toRange(line: number, char: number): DocumentRange {
+    return { line, charRange: { start: char, end: char + 1 } };
+  }
+
+  /** Check if given coordinates lies inside 80x25 grid */
+  private isInGrid(x: number, y: number): boolean {
+    return x >= 0 && x < ROWSIZE && y >= 0 && y < COLSIZE;
+  }
+}
diff --git a/engines/befunge93/tests/index.test.ts b/engines/befunge93/tests/index.test.ts
new file mode 100644
index 0000000..a712abf
--- /dev/null
+++ b/engines/befunge93/tests/index.test.ts
@@ -0,0 +1,96 @@
+import { readTestProgram, executeProgram } from "../../test-utils";
+import { Bfg93Direction } from "../constants";
+import Engine from "../runtime";
+
+/**
+ * All test programs are picked up from https://esolangs.org/wiki/Befunge,
+ * except the modifications mentioned alongside each test.
+ */
+
+/** Relative path to directory of sample programs */
+const DIRNAME = __dirname + "/samples";
+
+describe("Test programs", () => {
+  // Standard hello-world program
+  test("hello world", async () => {
+    const code = readTestProgram(DIRNAME, "helloworld");
+    const result = await executeProgram(new Engine(), code);
+    expect(result.output.charCodeAt(result.output.length - 1)).toBe(0);
+    expect(result.output.slice(0, -1)).toBe("Hello, World!");
+    expect(result.rendererState.direction).toBe(Bfg93Direction.DOWN);
+    expect(result.rendererState.stack.length).toBe(0);
+  });
+
+  // cat program
+  test("cat program", async () => {
+    const input = "abcd efgh\nijkl mnop\n";
+    const code = readTestProgram(DIRNAME, "cat");
+    const result = await executeProgram(new Engine(), code, input);
+    expect(result.output).toBe(input);
+    expect(result.rendererState.direction).toBe(Bfg93Direction.LEFT);
+    expect(result.rendererState.stack).toEqual([-1]);
+  });
+
+  // Random DNA printer
+  test("random DNA", async () => {
+    const code = readTestProgram(DIRNAME, "dna");
+    const result = await executeProgram(new Engine(), code);
+    // program prints "\r\n" at the end of output
+    expect(result.output.length).toBe(56 + 2);
+    expect(result.output.trim().search(/[^ATGC]/)).toBe(-1);
+    expect(result.rendererState.direction).toBe(Bfg93Direction.RIGHT);
+    expect(result.rendererState.stack).toEqual([0]);
+  });
+
+  // Factorial program
+  test("factorial", async () => {
+    const code = readTestProgram(DIRNAME, "factorial");
+    const result = await executeProgram(new Engine(), code, "5");
+    expect(result.output).toBe("120 ");
+    expect(result.rendererState.direction).toBe(Bfg93Direction.RIGHT);
+    expect(result.rendererState.stack.length).toBe(0);
+  });
+
+  // Sieve of Eratosthenes - prints prime nums upto 36
+  // (original prints up to 80, shortened here for testing purposes)
+  test("sieve of eratosthenes", async () => {
+    const code = readTestProgram(DIRNAME, "prime-sieve");
+    const result = await executeProgram(new Engine(), code);
+    const outputNums = result.output
+      .trim()
+      .split(" ")
+      .map((a) => parseInt(a, 10));
+    const primes = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31];
+    expect(outputNums).toEqual(primes);
+    expect(result.rendererState.direction).toBe(Bfg93Direction.DOWN);
+    expect(result.rendererState.stack).toEqual([37]);
+  });
+
+  // Quine 1 - simple single-line quine
+  test("simple singleline quine", async () => {
+    const code = readTestProgram(DIRNAME, "quine1");
+    const result = await executeProgram(new Engine(), code);
+    expect(result.output).toBe(code);
+    expect(result.rendererState.direction).toBe(Bfg93Direction.RIGHT);
+    expect(result.rendererState.stack).toEqual([44]);
+  });
+
+  // Quine 2 - multiline quine
+  test("multiline quine", async () => {
+    const code = readTestProgram(DIRNAME, "quine2");
+    const result = await executeProgram(new Engine(), code);
+    // Output has an extra space at the end - verified on tio.run
+    expect(result.output).toBe(code + " ");
+    expect(result.rendererState.direction).toBe(Bfg93Direction.LEFT);
+    expect(result.rendererState.stack).toEqual([0]);
+  });
+
+  // Quine 3 - quine without using "g"
+  test("quine without using 'g'", async () => {
+    const code = readTestProgram(DIRNAME, "quine3");
+    const result = await executeProgram(new Engine(), code);
+    expect(result.output).toBe(code);
+    expect(result.rendererState.direction).toBe(Bfg93Direction.LEFT);
+    expect(result.rendererState.stack).toEqual([0]);
+  });
+});
diff --git a/engines/befunge93/tests/samples/cat.txt b/engines/befunge93/tests/samples/cat.txt
new file mode 100644
index 0000000..61644b9
--- /dev/null
+++ b/engines/befunge93/tests/samples/cat.txt
@@ -0,0 +1 @@
+~:1+!#@_,
\ No newline at end of file
diff --git a/engines/befunge93/tests/samples/dna.txt b/engines/befunge93/tests/samples/dna.txt
new file mode 100644
index 0000000..15e1b30
--- /dev/null
+++ b/engines/befunge93/tests/samples/dna.txt
@@ -0,0 +1,8 @@
+7^DN>vA
+v_#v? v
+7^<""""
+3  ACGT
+90!""""
+4*:>>>v
++8^-1,<
+> ,+,@)
\ No newline at end of file
diff --git a/engines/befunge93/tests/samples/factorial.txt b/engines/befunge93/tests/samples/factorial.txt
new file mode 100644
index 0000000..8ce4081
--- /dev/null
+++ b/engines/befunge93/tests/samples/factorial.txt
@@ -0,0 +1,2 @@
+&>:1-:v v *_$.@ 
+ ^    _$>\:^
\ No newline at end of file
diff --git a/engines/befunge93/tests/samples/helloworld.txt b/engines/befunge93/tests/samples/helloworld.txt
new file mode 100644
index 0000000..3e2bb56
--- /dev/null
+++ b/engines/befunge93/tests/samples/helloworld.txt
@@ -0,0 +1,3 @@
+"!dlroW ,olleH">:v
+               |,<
+               @
\ No newline at end of file
diff --git a/engines/befunge93/tests/samples/prime-sieve.txt b/engines/befunge93/tests/samples/prime-sieve.txt
new file mode 100644
index 0000000..92e3224
--- /dev/null
+++ b/engines/befunge93/tests/samples/prime-sieve.txt
@@ -0,0 +1,4 @@
+2>:3g" "-!v\  g30          <
+ |!`"$":+1_:.:03p>03g+:"$"`|
+ @               ^  p3\" ":<
+2 234567890123456789012345678901234567890123456789012345678901234567890123456789
\ No newline at end of file
diff --git a/engines/befunge93/tests/samples/quine1.txt b/engines/befunge93/tests/samples/quine1.txt
new file mode 100644
index 0000000..e009c3a
--- /dev/null
+++ b/engines/befunge93/tests/samples/quine1.txt
@@ -0,0 +1 @@
+01->1# +# :# 0# g# ,# :# 5# 8# *# 4# +# -# _@
\ No newline at end of file
diff --git a/engines/befunge93/tests/samples/quine2.txt b/engines/befunge93/tests/samples/quine2.txt
new file mode 100644
index 0000000..786bf72
--- /dev/null
+++ b/engines/befunge93/tests/samples/quine2.txt
@@ -0,0 +1,2 @@
+0 v
+ "<@_ #! #: #,<*2-1*92,*84,*25,+*92*4*55.0                                      
\ No newline at end of file
diff --git a/engines/befunge93/tests/samples/quine3.txt b/engines/befunge93/tests/samples/quine3.txt
new file mode 100644
index 0000000..ef33c43
--- /dev/null
+++ b/engines/befunge93/tests/samples/quine3.txt
@@ -0,0 +1 @@
+<@,+2*48_,#! #:<,_$#-:#*8#4<8"
\ No newline at end of file
diff --git a/engines/execution-controller.ts b/engines/execution-controller.ts
index 9936c30..6ad5ed1 100644
--- a/engines/execution-controller.ts
+++ b/engines/execution-controller.ts
@@ -153,7 +153,7 @@ class ExecutionController<RS> {
           if (isRuntimeError(error)) {
             this._isPaused = true;
             resolve({
-              result: this._result!,
+              result: { ...this._result!, output: undefined },
               error: serializeRuntimeError(error),
             });
           } else reject(error);
diff --git a/pages/ide/befunge93.tsx b/pages/ide/befunge93.tsx
new file mode 100644
index 0000000..4ce0335
--- /dev/null
+++ b/pages/ide/befunge93.tsx
@@ -0,0 +1,26 @@
+import React from "react";
+import { NextPage } from "next";
+import Head from "next/head";
+import { Mainframe } from "../../ui/Mainframe";
+import { Header } from "../../ui/header";
+import LangProvider from "../../engines/befunge93";
+const LANG_ID = "befunge93";
+const LANG_NAME = "Befunge-93";
+
+const IDE: NextPage = () => {
+  return (
+    <>
+      <Head>
+        <title>{LANG_NAME} | Esolang Park</title>
+      </Head>
+      <div style={{ height: "100%", display: "flex", flexDirection: "column" }}>
+        <Header langName={LANG_NAME} />
+        <div style={{ flexGrow: 1 }}>
+          <Mainframe langName={LANG_ID} provider={LangProvider} />
+        </div>
+      </div>
+    </>
+  );
+};
+
+export default IDE;