diff --git a/index.html b/index.html
index 6229f8b..591db51 100644
--- a/index.html
+++ b/index.html
@@ -17,6 +17,6 @@
       <img id="img-jugadorx" src="/src/assets/eli.png">
     </div>
     <canvas id="juego" width="1280" height="720"></canvas>
-    <script type="module" src="/src/main.js"></script>
+    <script type="module" src="/src/main.ts"></script>
   </body>
 </html>
diff --git a/package.json b/package.json
index 2135909..b96958a 100644
--- a/package.json
+++ b/package.json
@@ -7,6 +7,7 @@
     "serve": "vite preview"
   },
   "devDependencies": {
+    "typescript": "^4.3.4",
     "vite": "^2.3.8"
   }
 }
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index d044be4..9a37824 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -1,9 +1,11 @@
 lockfileVersion: 5.3
 
 specifiers:
+  typescript: ^4.3.4
   vite: ^2.3.8
 
 devDependencies:
+  typescript: 4.3.4
   vite: 2.3.8
 
 packages:
@@ -81,6 +83,12 @@ packages:
     engines: {node: '>=0.10.0'}
     dev: true
 
+  /typescript/4.3.4:
+    resolution: {integrity: sha512-uauPG7XZn9F/mo+7MrsRjyvbxFpzemRjKEZXS4AK83oP2KKOJPvb+9cO/gmnv8arWZvhnjVOXz7B49m1l0e9Ew==}
+    engines: {node: '>=4.2.0'}
+    hasBin: true
+    dev: true
+
   /vite/2.3.8:
     resolution: {integrity: sha512-QiEx+iqNnJntSgSF2fWRQvRey9pORIrtNJzNyBJXwc+BdzWs83FQolX84cTBo393cfhObrtWa6180dAa4NLDiQ==}
     engines: {node: '>=12.0.0'}
diff --git a/src/jugando.js b/src/jugando.js
deleted file mode 100644
index 5e7c825..0000000
--- a/src/jugando.js
+++ /dev/null
@@ -1,96 +0,0 @@
-import { posInBox } from './utils.js'
-import { loadSprite } from './sprite.js'
-
-const larretaImg = document.getElementById("img-larreta")
-const inmobiliariaImg = document.getElementById("img-inmobiliaria")
-const veredaImg = document.getElementById("img-vereda")
-const fondoImg = document.getElementById("img-fondo")
-const jugadorxImg = document.getElementById("img-jugadorx")
-
-const jugadorxSprite = loadSprite(jugadorxImg, 133, 266)
-const larretaSprite = loadSprite(larretaImg, 800, 1069)
-
-export function createJugandoState() {
-  return {
-    current: 'jugando',
-    pos: { x: 3000 },
-    view: { x: 0 },
-    velocity: { x: 0 },
-    // { x }
-    enemies: [{ x: 2700 }],
-  }
-}
-
-export function updateJugando(juego, dt) {
-  juego.state.velocity.x = 0
-  if (juego.keyboard.keys.d) {
-    juego.state.velocity.x += 200
-  }
-  if (juego.keyboard.keys.a) {
-    juego.state.velocity.x -= 200
-  }
-
-  juego.state.pos.x += (dt / 1000) * juego.state.velocity.x
-  if (juego.state.pos.x < 2000) juego.state.pos.x = 2000
-  if (juego.state.pos.x > 4000) juego.state.pos.x = 4000
-
-  juego.state.view.x = -juego.state.pos.x + juego.canvas.width / 2 - jugadorxDimensions(juego).width / 2
-}
-
-function drawBackground(juego, y, height, img) {
-  const aspect = img.width / img.height
-  const width = height * aspect
-  const realY = typeof y === 'function' ? y(height) : y
-  for (let i = 0; i < 10; i++) {
-    juego.ctx.drawImage(img, i * (width - 1) + juego.state.view.x, realY, width, height)
-  }
-  return realY
-}
-
-function jugadorxDimensions(juego) {
-  const height = juego.canvas.height / 4
-  const width = jugadorxSprite.getWidth(height)
-  return { height, width }
-}
-
-function drawJugadorx(juego, floorY) {
-  const { height } = jugadorxDimensions(juego)
-  jugadorxSprite.draw(
-    juego,
-    juego.state.pos.x + juego.state.view.x,
-    floorY - height,
-    height,
-    0
-  )
-}
-
-function larretaDimensions(juego) {
-  const height = juego.canvas.height / 4
-  const width = larretaSprite.getWidth(height)
-  return { height, width }
-}
-
-function drawEnemies(juego, floorY) {
-  const { height } = larretaDimensions(juego)
-  for (const enemy of juego.state.enemies) {
-    larretaSprite.draw(
-      juego,
-      enemy.x + juego.state.view.x,
-      floorY - height,
-      height,
-      0
-    )
-  }
-}
-
-export function drawJugando(juego) {
-  const {width, height} = juego.canvas
-  const {x} = juego.state.pos
-
-  drawBackground(juego, 0, (height / 3) * 2, fondoImg)
-  const floorY = drawBackground(juego, h => height - h, height / 3, veredaImg)
-
-  drawEnemies(juego, floorY)
-
-  drawJugadorx(juego, floorY)
-}
diff --git a/src/jugando.ts b/src/jugando.ts
new file mode 100644
index 0000000..9524373
--- /dev/null
+++ b/src/jugando.ts
@@ -0,0 +1,93 @@
+import { loadSprite } from './sprite'
+import { Juego } from './main'
+
+const larretaImg = document.getElementById("img-larreta") as HTMLImageElement
+const inmobiliariaImg = document.getElementById("img-inmobiliaria") as HTMLImageElement
+const veredaImg = document.getElementById("img-vereda") as HTMLImageElement
+const fondoImg = document.getElementById("img-fondo") as HTMLImageElement
+const jugadorxImg = document.getElementById("img-jugadorx") as HTMLImageElement
+
+const jugadorxSprite = loadSprite(jugadorxImg, 133, 266, juego => juego.canvas.height / 4)
+const larretaSprite = loadSprite(larretaImg, 800, 1069, juego => juego.canvas.height / 4)
+
+export type State = {
+    current: "jugando"
+    pos: { x: number }
+    view: { x: number }
+    velocity: { x: number }
+    enemies: { x: number }[]
+}
+
+export function createJugandoState(): State {
+    return {
+        current: "jugando",
+        pos: { x: 3000 },
+        view: { x: 0 },
+        velocity: { x: 0 },
+        enemies: [{ x: 2700 }],
+    }
+}
+
+export function update(juego: Juego<State>, dt: number) {
+    juego.state.velocity.x = 0
+    if (juego.keyboard.keys.d) {
+        juego.state.velocity.x += 200
+    }
+    if (juego.keyboard.keys.a) {
+        juego.state.velocity.x -= 200
+    }
+
+    juego.state.pos.x += (dt / 1000) * juego.state.velocity.x
+    if (juego.state.pos.x < 2000) juego.state.pos.x = 2000
+    if (juego.state.pos.x > 4000) juego.state.pos.x = 4000
+
+    juego.state.view.x = -juego.state.pos.x + juego.canvas.width / 2 - jugadorxSprite.getWidth(juego) / 2
+}
+
+function drawBackground(
+    juego: Juego<State>,
+    y: number | ((height: number) => number),
+    height: number,
+    img: HTMLImageElement,
+) {
+    const aspect = img.width / img.height
+    const width = height * aspect
+    const realY = typeof y === 'function' ? y(height) : y
+    for (let i = 0; i < 10; i++) {
+        juego.ctx.drawImage(img, i * (width - 1) + juego.state.view.x, realY, width, height)
+    }
+    return realY
+}
+
+function drawJugadorx(juego: Juego<State>, floorY: number) {
+    const height = jugadorxSprite.getHeight(juego)
+    jugadorxSprite.draw(
+        juego,
+        juego.state.pos.x + juego.state.view.x,
+        floorY - height,
+        0
+    )
+}
+
+function drawEnemies(juego: Juego<State>, floorY: number) {
+    const height = larretaSprite.getHeight(juego)
+    for (const enemy of juego.state.enemies) {
+        larretaSprite.draw(
+            juego,
+            enemy.x + juego.state.view.x,
+            floorY - height,
+            0
+        )
+    }
+}
+
+export function draw(juego: Juego<State>, timestamp: number) {
+    const { height } = juego.canvas
+
+    drawBackground(juego, 0, (height / 3) * 2, fondoImg)
+    const floorY = drawBackground(juego, h => height - h, height / 3, veredaImg)
+
+    drawEnemies(juego, floorY)
+
+    drawJugadorx(juego, floorY)
+}
diff --git a/src/main.js b/src/main.js
deleted file mode 100644
index e98a8da..0000000
--- a/src/main.js
+++ /dev/null
@@ -1,85 +0,0 @@
-import './style.css'
-
-import { drawWelcome, updateWelcome } from "./welcome.js"
-import { drawJugando, updateJugando } from "./jugando.js"
-
-function update(juego, dt) {
-  switch (juego.state.current) {
-    case "welcome":
-      updateWelcome(juego, dt)
-      break
-    case "jugando":
-      updateJugando(juego, dt)
-      break
-  }
-}
-
-function draw(juego, timestamp) {
-  const {width, height} = juego.canvas
-
-  juego.ctx.clearRect(0, 0, width, height)
-  switch (juego.state.current) {
-    case "welcome":
-      drawWelcome(juego, timestamp)
-      break
-    case "jugando":
-      drawJugando(juego, timestamp)
-      break
-  }
-}
-
-function resizeCanvas(canvas) {
-  canvas.width = canvas.clientWidth
-  canvas.height = canvas.clientHeight
-}
-
-function initJuego() {
-  const canvas = document.querySelector("#juego")
-  const ctx = canvas.getContext("2d")
-
-  window.addEventListener("resize", () => resizeCanvas(canvas), false)
-  resizeCanvas(canvas)
-
-  let juego = {
-    canvas,
-    ctx,
-    mouse: { x: 0, y: 0, down: false },
-    keyboard: { keys: {} },
-    state: {
-      current: "welcome",
-    },
-  }
-  window.juego = juego
-
-  canvas.addEventListener("mousemove", e => {
-    juego.mouse.x = e.clientX
-    juego.mouse.y = e.clientY
-  }, false)
-  canvas.addEventListener("mousedown", e => {
-    juego.mouse.down = true
-  }, false)
-  canvas.addEventListener("mouseup", e => {
-    juego.mouse.down = false
-  }, false)
-  window.addEventListener("keydown", e => {
-    juego.keyboard.keys[e.key] = true
-  })
-  window.addEventListener("keyup", e => {
-    juego.keyboard.keys[e.key] = false
-  })
-
-  let lastRender = 0
-  function loop(timestamp) {
-    const progress = timestamp - lastRender
-
-    update(juego, progress)
-    draw(juego, timestamp)
-
-    lastRender = timestamp
-    window.requestAnimationFrame(loop)
-  }
-
-  window.requestAnimationFrame(loop)
-}
-initJuego()
-document.addEventListener("DOMDocumentLoaded", console.log)
diff --git a/src/main.ts b/src/main.ts
new file mode 100644
index 0000000..d32aaba
--- /dev/null
+++ b/src/main.ts
@@ -0,0 +1,94 @@
+import './style.css'
+
+import * as welcome from "./welcome"
+import * as jugando from "./jugando"
+
+function update(juego: Juego<any>, dt: number) {
+    switch (juego.state.current) {
+        case "welcome":
+            welcome.update(juego, dt)
+            break
+        case "jugando":
+            jugando.update(juego, dt)
+            break
+    }
+}
+
+function draw(juego: Juego<any>, timestamp: number) {
+    const { width, height } = juego.canvas
+
+    juego.ctx.clearRect(0, 0, width, height)
+    switch (juego.state.current) {
+        case "welcome":
+            welcome.draw(juego, timestamp)
+            break
+        case "jugando":
+            jugando.draw(juego, timestamp)
+            break
+    }
+}
+
+function resizeCanvas(canvas: HTMLCanvasElement) {
+    canvas.width = canvas.clientWidth
+    canvas.height = canvas.clientHeight
+}
+
+export type State = welcome.State | jugando.State
+
+export type Juego<T extends State> = {
+    canvas: HTMLCanvasElement
+    ctx: CanvasRenderingContext2D
+    mouse: { x: number; y: number, down: boolean }
+    keyboard: { keys: { [key: string]: boolean } }
+    state: T
+}
+
+function initJuego() {
+    const canvas = document.querySelector<HTMLCanvasElement>("#juego")!
+    const ctx = canvas.getContext("2d")!
+
+    window.addEventListener("resize", () => resizeCanvas(canvas), false)
+    resizeCanvas(canvas)
+
+    let juego: Juego<welcome.State> = {
+        canvas,
+        ctx,
+        mouse: { x: 0, y: 0, down: false },
+        keyboard: { keys: {} },
+        state: {
+            current: "welcome",
+        },
+    };
+    (window as any).juego = juego
+
+    canvas.addEventListener("mousemove", e => {
+        juego.mouse.x = e.clientX
+        juego.mouse.y = e.clientY
+    }, false)
+    canvas.addEventListener("mousedown", () => {
+        juego.mouse.down = true
+    }, false)
+    canvas.addEventListener("mouseup", () => {
+        juego.mouse.down = false
+    }, false)
+    window.addEventListener("keydown", e => {
+        juego.keyboard.keys[e.key] = true
+    })
+    window.addEventListener("keyup", e => {
+        juego.keyboard.keys[e.key] = false
+    })
+
+    let lastRender = 0
+    const loop: FrameRequestCallback = timestamp => {
+        const progress = timestamp - lastRender
+
+        update(juego, progress)
+        draw(juego, timestamp)
+
+        lastRender = timestamp
+        window.requestAnimationFrame(loop)
+    }
+
+    window.requestAnimationFrame(loop)
+}
+initJuego()
diff --git a/src/sprite.js b/src/sprite.js
deleted file mode 100644
index 4cec233..0000000
--- a/src/sprite.js
+++ /dev/null
@@ -1,34 +0,0 @@
-// Sprites are image assets that have a grid of tiles to animate
-// width/height are the size of each tile
-// drawHeight is a function that takes juego
-export function loadSprite(img, width, height, drawHeight) {
-
-  const aspect = width / height
-  const rowSize = Math.floor(img.width / width)
-  if (img.width / width !== rowSize) {
-    console.warn(`The sprite grid for ${img.src} has extra space after rows, are you sure the width/height of each tile is right?`)
-  }
-
-  function getWidth(drawHeight) {
-    return drawHeight * aspect
-  }
-
-  function draw(juego, x, y, drawHeight, num) {
-    const drawWidth = getWidth(drawHeight)
-    const tileX = num % rowSize
-    const tileY = Math.floor(num / rowSize)
-    juego.ctx.drawImage(
-      img,
-      tileX * width,
-      tileY * height,
-      width,
-      height,
-      x,
-      y,
-      drawWidth,
-      drawHeight,
-    )
-  }
-
-  return { getWidth, draw, width, height }
-}
diff --git a/src/sprite.ts b/src/sprite.ts
new file mode 100644
index 0000000..758fd69
--- /dev/null
+++ b/src/sprite.ts
@@ -0,0 +1,41 @@
+import { Juego } from "./main"
+
+// Sprites are image assets that have a grid of tiles to animate
+export function loadSprite(
+    img: HTMLImageElement,
+    // The size of each tile
+    width: number, height: number,
+    // Calculates the size of the sprite when rendering
+    getHeight: (juego: Juego<any>) => number,
+) {
+
+    const aspect = width / height
+    const rowSize = Math.floor(img.width / width)
+    if (img.width / width !== rowSize) {
+        console.warn(`The sprite grid for ${img.src} has extra space after rows, are you sure the width/height of each tile is right?`)
+    }
+
+    function getWidth(juego: Juego<any>) {
+        return getHeight(juego) * aspect
+    }
+
+    function draw(juego: Juego<any>, x: number, y: number, spriteIndex: number) {
+        const drawHeight = getHeight(juego)
+        const drawWidth = getWidth(juego)
+        const tileX = spriteIndex % rowSize
+        const tileY = Math.floor(spriteIndex / rowSize)
+        juego.ctx.drawImage(
+            img,
+            tileX * width,
+            tileY * height,
+            width,
+            height,
+            x,
+            y,
+            drawWidth,
+            drawHeight,
+        )
+    }
+
+    return { getWidth, getHeight, draw, width, height }
+}
diff --git a/src/utils.js b/src/utils.js
deleted file mode 100644
index 0de64dc..0000000
--- a/src/utils.js
+++ /dev/null
@@ -1,5 +0,0 @@
-export function posInBox(box, pos) {
-  return pos.x > box.x && pos.x < box.x + box.width
-    && pos.y > box.y && pos.y < box.y + box.height
-}
-
diff --git a/src/utils.ts b/src/utils.ts
new file mode 100644
index 0000000..b974041
--- /dev/null
+++ b/src/utils.ts
@@ -0,0 +1,11 @@
+export type Pos = {
+    x: number, y: number,
+};
+export type Box = Pos & {
+    width: number, height: number,
+}
+export function posInBox(box: Box, pos: Pos) {
+    return pos.x > box.x && pos.x < box.x + box.width
+        && pos.y > box.y && pos.y < box.y + box.height
+}
+
diff --git a/src/welcome.js b/src/welcome.js
deleted file mode 100644
index 4254336..0000000
--- a/src/welcome.js
+++ /dev/null
@@ -1,27 +0,0 @@
-import { posInBox } from './utils.js'
-import { createJugandoState } from './jugando.js'
-
-const img = document.getElementById("img-boton-jugar")
-
-function startButton(juego) {
-  const [width, height] = [img.width / 4, img.height / 4]
-  return {
-    x: juego.canvas.width - width - 30,
-    y: juego.canvas.height - height - 30,
-    width,
-    height,
-  }
-}
-
-export function updateWelcome(juego, dt) {
-  const btn = startButton(juego)
-  if (juego.mouse.down && posInBox(btn, juego.mouse)) {
-    juego.state = createJugandoState()
-  }
-}
-
-export function drawWelcome(juego) {
-  const {width, height} = juego.canvas
-  const btn = startButton(juego)
-  juego.ctx.drawImage(img, btn.x, btn.y, btn.width, btn.height)
-}
diff --git a/src/welcome.ts b/src/welcome.ts
new file mode 100644
index 0000000..7cb44bf
--- /dev/null
+++ b/src/welcome.ts
@@ -0,0 +1,31 @@
+import { posInBox } from './utils'
+import { createJugandoState } from './jugando'
+import { Juego } from './main'
+
+const img = document.getElementById("img-boton-jugar") as HTMLImageElement
+
+export type State = {
+    current: "welcome"
+}
+
+function startButton(juego: Juego<State>) {
+    const [width, height] = [img.width / 4, img.height / 4]
+    return {
+        x: juego.canvas.width - width - 30,
+        y: juego.canvas.height - height - 30,
+        width,
+        height,
+    }
+}
+
+export function update(juego: Juego<State>, dt: number) {
+    const btn = startButton(juego)
+    if (juego.mouse.down && posInBox(btn, juego.mouse)) {
+        (juego as Juego<any>).state = createJugandoState()
+    }
+}
+
+export function draw(juego: Juego<State>, timestamp: number) {
+    const btn = startButton(juego)
+    juego.ctx.drawImage(img, btn.x, btn.y, btn.width, btn.height)
+}
diff --git a/tsconfig.json b/tsconfig.json
new file mode 100644
index 0000000..ad7827c
--- /dev/null
+++ b/tsconfig.json
@@ -0,0 +1,15 @@
+{
+    "compilerOptions": {
+        "target": "ESNext",
+        "module": "ESNext",
+        "lib": ["ESNext", "DOM"],
+        "moduleResolution": "Node",
+        "strict": true,
+        "sourceMap": true,
+        "resolveJsonModule": true,
+        "esModuleInterop": true,
+        "noEmit": true,
+        "noImplicitReturns": true
+    },
+    "include": ["./src"]
+}
