diff --git a/src/client/app.tsx b/src/client/app.tsx
index 0222f29..aeead01 100644
--- a/src/client/app.tsx
+++ b/src/client/app.tsx
@@ -27,6 +27,10 @@ theo (heh) "Oh, hey guys. Didn't see you there."
 
 axelle "Oh, hi."
 
+board "If $ax^2+bx+c=0$, then $x = \frac{-b\pm\sqrt{b^2-4ac}}{2a}$."
+
+theo "Did you know..."
+
 `
 
 const App = (props: { name: string }) => {
diff --git a/src/client/player/DialogText.tsx b/src/client/player/DialogText.tsx
new file mode 100644
index 0000000..8b13ad8
--- /dev/null
+++ b/src/client/player/DialogText.tsx
@@ -0,0 +1,26 @@
+import { useEffect, useLayoutEffect, useState } from "react";
+import { MathText } from "./MathText";
+
+export const DialogText = (props: {children: string, onFinish?: () => void}) => {
+	const {children: str, onFinish} = props;
+	const [len, setLen] = useState(0);
+
+	useLayoutEffect(() => {
+		setLen(0);
+	}, [str]);
+
+	useEffect(() => {
+		const interval = setInterval(() => {
+			setLen(l => Math.max(l+1, str.length));
+		}, 70);
+		if (len === str.length) {
+			// clearInterval(interval);
+			onFinish && onFinish();
+		}
+		return () => {
+			clearInterval(interval);
+		};
+	}, [str, setLen]);
+
+	return <MathText>{str.slice(0, len)}</MathText>
+}
\ No newline at end of file
diff --git a/src/client/player/MathText.tsx b/src/client/player/MathText.tsx
index e34f6ef..98ff45a 100644
--- a/src/client/player/MathText.tsx
+++ b/src/client/player/MathText.tsx
@@ -11,7 +11,7 @@ export const Katex = (props: { tex: string }) => {
 		if (!element) {
 			return;
 		}
-		katex.render(tex, element);
+		katex.render(tex, element, {throwOnError: false});
 	}, [tex])
 	return (
 		<span ref={ref} className={css`
diff --git a/src/client/player/Player.tsx b/src/client/player/Player.tsx
index 0d79a55..b0a0f49 100644
--- a/src/client/player/Player.tsx
+++ b/src/client/player/Player.tsx
@@ -3,6 +3,7 @@ import { Mathuscript, parseMathuscript } from "./parse";
 import { useCallback, useMemo, useRef, useState } from "react";
 import { MathText } from "./MathText";
 import { characterData } from "./data";
+import { DialogText } from "./DialogText";
 
 type VisualState = {
 	turn: number,
@@ -63,8 +64,13 @@ export const MathuscriptPlayer = (props: { script: string }) => {
 	const goAgain = useRef(false);
 	const [index, setIndex] = useState(0);
 	const [visualState, setVisualState] = useState<VisualState>({turn: 0, characters: [], board: {text: ""}, dialog: {name: null, emotion: "", text: ""}});
+	const dialogGoing = useRef(false);
 
 	const doStep = useCallback(() => {
+		if (dialogGoing.current) {
+			console.log("dialog running");
+			return;
+		}
 		goAgain.current = false;
 		const step = parsedScript[index];
 		if (step) {
@@ -73,6 +79,8 @@ export const MathuscriptPlayer = (props: { script: string }) => {
 			setVisualState(state => afterStep(state, step));
 			if (step.name === "board") {
 				goAgain.current = true;
+			} else {
+				dialogGoing.current = true;
 			}
 		} else {
 			console.log("the end");
@@ -109,7 +117,7 @@ export const MathuscriptPlayer = (props: { script: string }) => {
 					display: flex;
 					align-items: center;
 					justify-content: center;
-					color: white;
+					color: hsla(70, 60%, 95%, 0.9);
 					padding: 1em;
 				`}>
 					<div>
@@ -122,6 +130,7 @@ export const MathuscriptPlayer = (props: { script: string }) => {
 					height: 100%;
 					top: 0;
 					left: 0;
+					--char-pos: ${visualState.board.text ? '-20%' : '0'};
 				`}>
 					{
 						allChars.map(name => {
@@ -136,10 +145,10 @@ export const MathuscriptPlayer = (props: { script: string }) => {
 								<img
 									className={css`
 										position: absolute;
-										transition: all 0.25s;
+										transition: all 0.35s;
 										left: -60%;
 										${
-											visChar?.side === "left" && css`left: -20%;`
+											visChar?.side === "left" && css`left: var(--char-pos);`
 										}
 										height: 90%;
 										bottom: 0;
@@ -156,11 +165,11 @@ export const MathuscriptPlayer = (props: { script: string }) => {
 								<img
 									className={css`
 										position: absolute;
-										transition: all 0.25s;
+										transition: all 0.35s;
 										transform: scaleX(-1);
 										right: -60%;
 										${
-											visChar?.side === "right" && css`right: -20%;`
+											visChar?.side === "right" && css`right: var(--char-pos);`
 										}
 										height: 90%;
 										bottom: 0;
@@ -207,9 +216,13 @@ export const MathuscriptPlayer = (props: { script: string }) => {
 									border-radius: 0.25em;
 									padding: 0.25em 0.75em;
 								`}>
-									<strong>{speakingChar.displayName}</strong>
+									<strong>{speakingChar.displayName}</strong> {visualState.dialog.emotion && <em>({visualState.dialog.emotion})</em>}
 								</div>
-								{visualState.dialog.emotion && <em>({visualState.dialog.emotion})</em>} <MathText>{visualState.dialog.text}</MathText>
+								<DialogText onFinish={() => {
+									dialogGoing.current = false;
+								}}>
+									{visualState.dialog.text}
+								</DialogText>
 							</>
 						}
 					</div>