diff --git a/assets/audio/Infinite_Spankage_M.mp3 b/assets/audio/Infinite_Spankage_M.mp3
new file mode 100644
index 0000000..7bf2b7a
Binary files /dev/null and b/assets/audio/Infinite_Spankage_M.mp3 differ
diff --git a/assets/images/coin-frames.png b/assets/images/coin-frames.png
new file mode 100644
index 0000000..217c705
Binary files /dev/null and b/assets/images/coin-frames.png differ
diff --git a/assets/images/crawl-frames.png b/assets/images/crawl-frames.png
new file mode 100644
index 0000000..b6868d5
Binary files /dev/null and b/assets/images/crawl-frames.png differ
diff --git a/assets/images/death-normal-frames.png b/assets/images/death-normal-frames.png
new file mode 100644
index 0000000..350d3ca
Binary files /dev/null and b/assets/images/death-normal-frames.png differ
diff --git a/assets/images/electrecuted-frames.png b/assets/images/electrecuted-frames.png
new file mode 100644
index 0000000..3329b01
Binary files /dev/null and b/assets/images/electrecuted-frames.png differ
diff --git a/assets/images/jump-frames.png b/assets/images/jump-frames.png
new file mode 100644
index 0000000..a02d755
Binary files /dev/null and b/assets/images/jump-frames.png differ
diff --git a/assets/images/kick-frames.png b/assets/images/kick-frames.png
new file mode 100644
index 0000000..6589fc9
Binary files /dev/null and b/assets/images/kick-frames.png differ
diff --git a/assets/images/run-frames.png b/assets/images/run-frames.png
index e049169..79df4a8 100644
Binary files a/assets/images/run-frames.png and b/assets/images/run-frames.png differ
diff --git a/lib/Runner.dart b/lib/Runner.dart
new file mode 100644
index 0000000..9641164
--- /dev/null
+++ b/lib/Runner.dart
@@ -0,0 +1,277 @@
+import 'package:firo_runner/main.dart';
+import 'package:flame/effects.dart';
+import 'package:flutter/material.dart';
+
+import 'package:flame/components.dart';
+
+enum RunnerState {
+  run,
+  jump,
+  duck,
+  kick,
+  float,
+  fall,
+  die,
+  electro,
+}
+
+class Runner extends Component with HasGameRef<MyGame> {
+  late SpriteAnimationGroupComponent sprite;
+
+  void setPosition(Vector2 position) {
+    sprite.position = position;
+  }
+
+  void setSize(Vector2 size, double ySize) {
+    // runnerSize = size;
+    sprite.size = size;
+  }
+
+  Sprite getSprite() {
+    return sprite.animation!.getSprite();
+  }
+
+  @override
+  void render(Canvas c) {
+    super.render(c);
+    getSprite().render(c, position: sprite.position, size: sprite.size);
+  }
+
+  int level = 7;
+
+  void updateLevel() {
+    level = (sprite.position.y / gameRef.blockSize).round();
+  }
+
+  String runnerState = "run";
+
+  void event(String event) {
+    print(event);
+    switch (event) {
+      case "jump":
+        runnerState = event;
+        sprite.current = RunnerState.jump;
+        sprite.addEffect(MoveEffect(
+          path: [
+            // sprite.position,
+            Vector2(sprite.x, (level - 1) * gameRef.blockSize),
+          ],
+          speed: 50,
+          curve: Curves.bounceIn,
+          onComplete: () {
+            updateLevel();
+            runnerState = "run";
+          },
+        ));
+        break;
+      case "doublejump":
+        runnerState = event;
+        sprite.current = RunnerState.jump;
+        sprite.addEffect(MoveEffect(
+          path: [
+            sprite.position,
+            Vector2(sprite.x, (level - 2) * gameRef.blockSize),
+          ],
+          speed: 50,
+          curve: Curves.bounceIn,
+          onComplete: () {
+            updateLevel();
+            runnerState = "run";
+          },
+        ));
+        break;
+      case "fall":
+        runnerState = event;
+        sprite.current = RunnerState.fall;
+        // TODO calculate distance to next platform.
+        sprite.addEffect(MoveEffect(
+          path: [
+            // sprite.position,
+            Vector2(sprite.x, (level + 1) * gameRef.blockSize),
+          ],
+          speed: 50,
+          curve: Curves.ease,
+          onComplete: updateLevel,
+        ));
+        break;
+      case "kick":
+        runnerState = event;
+        sprite.current = RunnerState.kick;
+        break;
+      case "run":
+        runnerState = event;
+        sprite.current = RunnerState.run;
+        break;
+      case "float":
+        runnerState = event;
+        sprite.current = RunnerState.float;
+        sprite.addEffect(MoveEffect(
+          path: [
+            sprite.position,
+            Vector2(sprite.x, (level - 1) * gameRef.blockSize),
+          ],
+          speed: 50,
+          curve: Curves.ease,
+          onComplete: () {
+            updateLevel();
+            runnerState = event;
+            sprite.current = RunnerState.float;
+          },
+        ));
+        break;
+      case "duck":
+        runnerState = event;
+        sprite.current = RunnerState.duck;
+        break;
+      case "die":
+        runnerState = event;
+        sprite.current = RunnerState.die;
+        break;
+      case "electro":
+        runnerState = event;
+        sprite.current = RunnerState.electro;
+        break;
+      default:
+        break;
+    }
+  }
+
+  void control(String input) {
+    print(runnerState + " " + input);
+    print(sprite.position);
+    switch (input) {
+      case "up":
+        if (runnerState == "run") {
+          event("jump");
+        } else if (runnerState == "jump") {
+          event("doublejump");
+        } else if (runnerState == "duck") {
+          event("run");
+        }
+        break;
+      case "down":
+        if (runnerState == "run") {
+          event("duck");
+        } else if (runnerState == "float") {
+          event("fall");
+        }
+        break;
+      case "right":
+        if (runnerState == "run") {
+          event("kick");
+        }
+        break;
+      case "center":
+        if (runnerState == "jump") {
+          event("float");
+        }
+        break;
+    }
+  }
+
+  @override
+  void update(double dt) {
+    super.update(dt);
+    // If the animation is finished
+    if (sprite.animation?.done() ?? false) {
+      sprite.animation!.reset();
+      if (runnerState == "kick") {
+        event("run");
+      }
+      sprite.current = RunnerState.run;
+    }
+
+    sprite.update(dt);
+  }
+
+  Future load(loadSpriteAnimation) async {
+    SpriteAnimation running = await loadSpriteAnimation(
+      'run-frames.png',
+      SpriteAnimationData.sequenced(
+        amount: 7,
+        stepTime: 0.1,
+        textureSize: Vector2(512, 512),
+      ),
+    );
+
+    SpriteAnimation jumping = await loadSpriteAnimation(
+      'jump-frames.png',
+      SpriteAnimationData.sequenced(
+        amount: 5,
+        stepTime: 0.1,
+        textureSize: Vector2(512, 512),
+        loop: false,
+      ),
+    );
+
+    SpriteAnimation ducking = await loadSpriteAnimation(
+      'crawl-frames.png',
+      SpriteAnimationData.sequenced(
+        amount: 3,
+        stepTime: 0.1,
+        textureSize: Vector2(512, 512),
+      ),
+    );
+
+    SpriteAnimation kicking = await loadSpriteAnimation(
+      'kick-frames.png',
+      SpriteAnimationData.sequenced(
+        amount: 13,
+        stepTime: 0.03,
+        textureSize: Vector2(512, 512),
+        loop: false,
+      ),
+    );
+
+    SpriteAnimation floating = await loadSpriteAnimation(
+      'run-frames.png',
+      SpriteAnimationData.sequenced(
+        amount: 1,
+        stepTime: 0.1,
+        textureSize: Vector2(512, 512),
+      ),
+    );
+
+    SpriteAnimation falling = await loadSpriteAnimation(
+      'run-frames.png',
+      SpriteAnimationData.sequenced(
+        amount: 1,
+        stepTime: 0.1,
+        textureSize: Vector2(512, 512),
+      ),
+    );
+
+    SpriteAnimation dieing = await loadSpriteAnimation(
+      'death-normal-frames.png',
+      SpriteAnimationData.sequenced(
+        amount: 20,
+        stepTime: 0.1,
+        textureSize: Vector2(512, 512),
+        loop: false,
+      ),
+    );
+
+    SpriteAnimation dieingElectorcuted = await loadSpriteAnimation(
+      'electrecuted-frames.png',
+      SpriteAnimationData.sequenced(
+        amount: 2,
+        stepTime: 0.1,
+        textureSize: Vector2(512, 512),
+      ),
+    );
+
+    sprite = SpriteAnimationGroupComponent(
+      animations: {
+        RunnerState.run: running,
+        RunnerState.jump: jumping,
+        RunnerState.duck: ducking,
+        RunnerState.kick: kicking,
+        RunnerState.float: floating,
+        RunnerState.fall: falling,
+        RunnerState.die: dieing,
+        RunnerState.electro: dieingElectorcuted,
+      },
+      current: RunnerState.run,
+    );
+  }
+}
diff --git a/lib/main.dart b/lib/main.dart
index b9bfc38..8c7649a 100644
--- a/lib/main.dart
+++ b/lib/main.dart
@@ -1,113 +1,199 @@
+import 'package:flame/components.dart';
+import 'package:flame/extensions.dart';
+import 'package:flame/flame.dart';
+import 'package:flame/game.dart';
+import 'package:flame/gestures.dart';
+import 'package:flame/keyboard.dart';
+import 'package:flame/palette.dart';
+import 'package:flame_audio/flame_audio.dart';
 import 'package:flutter/material.dart';
+import 'package:flame_audio/bgm.dart';
+import 'package:flutter/services.dart';
+import 'Runner.dart';
 
-void main() {
-  runApp(MyApp());
+const COLOR = const Color(0xFFDDC0A3);
+const SIZE = 52.0;
+const GRAVITY = 400.0;
+const BOOST = -380.0;
+
+void main() async {
+  WidgetsFlutterBinding.ensureInitialized();
+  await Flame.device.fullScreen();
+  await Flame.device.setLandscape();
+  final myGame = MyGame();
+  runApp(GameWidget(game: myGame));
 }
 
-class MyApp extends StatelessWidget {
-  // This widget is the root of your application.
+class MyGame extends BaseGame with PanDetector, TapDetector, KeyboardEvents {
+  TextPaint textPaint = TextPaint(
+    config: TextPaintConfig(fontSize: 48.0),
+  );
+
+  late Sprite background1;
+  late Sprite background2;
+  late Runner runner;
+  late var background;
+  late var platform1;
+  late var platform2;
+  late var platform3;
+  late var wire;
+  late var bug;
+  late var coin;
+
+  var runnerPosition = Vector2(0, 0);
+  var runnerSize;
+  var backgroundSize;
+  var background1Position;
+  var background2Position;
+  late double blockSize;
+
   @override
-  Widget build(BuildContext context) {
-    return MaterialApp(
-      title: 'Flutter Demo',
-      theme: ThemeData(
-        // This is the theme of your application.
-        //
-        // Try running your application with "flutter run". You'll see the
-        // application has a blue toolbar. Then, without quitting the app, try
-        // changing the primarySwatch below to Colors.green and then invoke
-        // "hot reload" (press "r" in the console where you ran "flutter run",
-        // or simply save your changes to "hot reload" in a Flutter IDE).
-        // Notice that the counter didn't reset back to zero; the application
-        // is not restarted.
-        primarySwatch: Colors.blue,
-      ),
-      home: MyHomePage(title: 'Flutter Demo Home Page'),
+  Future<void> onLoad() async {
+    print("load");
+    FlameAudio.bgm.initialize();
+    background = await Flame.images.load('bg.png');
+    background1 = Sprite(background);
+    background2 = Sprite(background);
+    platform1 = await Flame.images.load('platform1.png');
+    platform2 = await Flame.images.load('platform2.png');
+    platform3 = await Flame.images.load('platform3.png');
+    wire = await Flame.images.load('wire.png');
+    bug = await Flame.images.load('bug.png');
+    coin = await Flame.images.load('coin.png');
+
+    runner = Runner();
+    await runner.load(loadSpriteAnimation);
+    runner.setSize(runnerSize, blockSize);
+    runnerPosition = Vector2(blockSize, blockSize * 7);
+    runner.setPosition(runnerPosition);
+    add(runner);
+
+    FlameAudio.bgm.play('Infinite_Spankage_M.mp3');
+  }
+
+  @override
+  void render(Canvas canvas) {
+    background1.render(
+      canvas,
+      position: Vector2(0, 0),
+      size: backgroundSize,
     );
-  }
-}
-
-class MyHomePage extends StatefulWidget {
-  MyHomePage({Key? key, required this.title}) : super(key: key);
-
-  // This widget is the home page of your application. It is stateful, meaning
-  // that it has a State object (defined below) that contains fields that affect
-  // how it looks.
-
-  // This class is the configuration for the state. It holds the values (in this
-  // case the title) provided by the parent (in this case the App widget) and
-  // used by the build method of the State. Fields in a Widget subclass are
-  // always marked "final".
-
-  final String title;
-
-  @override
-  _MyHomePageState createState() => _MyHomePageState();
-}
-
-class _MyHomePageState extends State<MyHomePage> {
-  int _counter = 0;
-
-  void _incrementCounter() {
-    setState(() {
-      // This call to setState tells the Flutter framework that something has
-      // changed in this State, which causes it to rerun the build method below
-      // so that the display can reflect the updated values. If we changed
-      // _counter without calling setState(), then the build method would not be
-      // called again, and so nothing would appear to happen.
-      _counter++;
-    });
+    super.render(canvas);
+    final fpsCount = fps(1);
+    // textPaint.render(
+    //   canvas,
+    //   fpsCount.toString(),
+    //   Vector2(0, 0),
+    // );
   }
 
   @override
-  Widget build(BuildContext context) {
-    // This method is rerun every time setState is called, for instance as done
-    // by the _incrementCounter method above.
-    //
-    // The Flutter framework has been optimized to make rerunning build methods
-    // fast, so that you can just rebuild anything that needs updating rather
-    // than having to individually change instances of widgets.
-    return Scaffold(
-      appBar: AppBar(
-        // Here we take the value from the MyHomePage object that was created by
-        // the App.build method, and use it to set our appbar title.
-        title: Text(widget.title),
-      ),
-      body: Center(
-        // Center is a layout widget. It takes a single child and positions it
-        // in the middle of the parent.
-        child: Column(
-          // Column is also a layout widget. It takes a list of children and
-          // arranges them vertically. By default, it sizes itself to fit its
-          // children horizontally, and tries to be as tall as its parent.
-          //
-          // Invoke "debug painting" (press "p" in the console, choose the
-          // "Toggle Debug Paint" action from the Flutter Inspector in Android
-          // Studio, or the "Toggle Debug Paint" command in Visual Studio Code)
-          // to see the wireframe for each widget.
-          //
-          // Column has various properties to control how it sizes itself and
-          // how it positions its children. Here we use mainAxisAlignment to
-          // center the children vertically; the main axis here is the vertical
-          // axis because Columns are vertical (the cross axis would be
-          // horizontal).
-          mainAxisAlignment: MainAxisAlignment.center,
-          children: <Widget>[
-            Text(
-              'You have pushed the button this many times:',
-            ),
-            Text(
-              '$_counter',
-              style: Theme.of(context).textTheme.headline4,
-            ),
-          ],
-        ),
-      ),
-      floatingActionButton: FloatingActionButton(
-        onPressed: _incrementCounter,
-        tooltip: 'Increment',
-        child: Icon(Icons.add),
-      ), // This trailing comma makes auto-formatting nicer for build methods.
+  void update(double dt) {
+    super.update(dt);
+  }
+
+  @override
+  void onResize(Vector2 size) {
+    super.onResize(size);
+    blockSize = size.y / 9;
+    print(blockSize);
+    runnerSize = Vector2(
+      size.y / 9,
+      size.y / 9,
     );
+
+    backgroundSize = Vector2(size.x * 2, size.y);
+  }
+
+  // Mobile controls
+  late List<double> xdeltas;
+  late List<double> ydeltas;
+  @override
+  void onPanStart(DragStartInfo info) {
+    xdeltas = List.empty(growable: true);
+    ydeltas = List.empty(growable: true);
+  }
+
+  @override
+  void onPanUpdate(DragUpdateInfo info) {
+    xdeltas.add(info.delta.game.x);
+    ydeltas.add(info.delta.game.y);
+  }
+
+  @override
+  void onPanEnd(DragEndInfo info) {
+    double xdelta = xdeltas.isEmpty
+        ? 0
+        : xdeltas.reduce((value, element) => value + element);
+    double ydelta = ydeltas.isEmpty
+        ? 0
+        : ydeltas.reduce((value, element) => value + element);
+    if (xdelta.abs() > ydelta.abs()) {
+      if (xdelta > 0) {
+        runner.control("right");
+      } else {
+        runner.control("left");
+      }
+    } else if (xdelta.abs() < ydelta.abs()) {
+      if (ydelta > 0) {
+        runner.control("down");
+      } else {
+        runner.control("up");
+      }
+    }
+  }
+
+  @override
+  void onTap() {
+    runner.control("center");
+  }
+
+  // Keyboard controls.
+  var keyboardKey;
+  @override
+  void onKeyEvent(RawKeyEvent event) {
+    if (event is RawKeyUpEvent) {
+      keyboardKey = null;
+      switch (event.data.keyLabel) {
+        case "w":
+          runner.control("up");
+          break;
+        case "a":
+          runner.control("left");
+          break;
+        case "s":
+          runner.control("down");
+          break;
+        case "d":
+          runner.control("right");
+          break;
+        default:
+          if (event.data.logicalKey.keyId == 32) {
+            runner.control("down");
+          }
+          break;
+      }
+    }
+    if (event is RawKeyDownEvent && event.data.logicalKey.keyId == 32) {
+      if (keyboardKey == null) {
+        runner.control("center");
+      }
+      keyboardKey = "spacebar";
+    }
   }
 }
+
+class Background extends Component {
+  static final Paint _paint = Paint()..color = COLOR;
+  final size;
+
+  Background(this.size);
+
+  @override
+  void render(Canvas c) {
+    c.drawRect(Rect.fromLTWH(0.0, 0.0, size.x, size.y), _paint);
+  }
+
+  @override
+  void update(double t);
+}
diff --git a/pubspec.lock b/pubspec.lock
index 1757435..d72f0a9 100644
--- a/pubspec.lock
+++ b/pubspec.lock
@@ -14,7 +14,7 @@ packages:
       name: audioplayers
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "0.18.3"
+    version: "0.19.1"
   boolean_selector:
     dependency: transitive
     description:
@@ -22,13 +22,6 @@ packages:
       url: "https://pub.dartlang.org"
     source: hosted
     version: "2.1.0"
-  box2d_flame:
-    dependency: transitive
-    description:
-      name: box2d_flame
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "0.4.6"
   characters:
     dependency: transitive
     description:
@@ -57,13 +50,6 @@ packages:
       url: "https://pub.dartlang.org"
     source: hosted
     version: "1.15.0"
-  convert:
-    dependency: transitive
-    description:
-      name: convert
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "2.1.1"
   crypto:
     dependency: transitive
     description:
@@ -105,21 +91,14 @@ packages:
       name: flame
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "0.29.4"
-  flare_dart:
-    dependency: transitive
+    version: "1.0.0-releasecandidate.13"
+  flame_audio:
+    dependency: "direct main"
     description:
-      name: flare_dart
+      name: flame_audio
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "2.3.4"
-  flare_flutter:
-    dependency: transitive
-    description:
-      name: flare_flutter
-      url: "https://pub.dartlang.org"
-    source: hosted
-    version: "2.0.6"
+    version: "1.0.0-rc.1"
   flutter:
     dependency: "direct main"
     description: flutter
@@ -135,6 +114,20 @@ packages:
     description: flutter
     source: sdk
     version: "0.0.0"
+  http:
+    dependency: transitive
+    description:
+      name: http
+      url: "https://pub.dartlang.org"
+    source: hosted
+    version: "0.13.3"
+  http_parser:
+    dependency: transitive
+    description:
+      name: http_parser
+      url: "https://pub.dartlang.org"
+    source: hosted
+    version: "4.0.0"
   js:
     dependency: transitive
     description:
@@ -162,7 +155,7 @@ packages:
       name: ordered_set
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "2.0.2"
+    version: "3.2.0"
   path:
     dependency: transitive
     description:
@@ -176,7 +169,7 @@ packages:
       name: path_provider
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "2.0.2"
+    version: "2.0.3"
   path_provider_linux:
     dependency: transitive
     description:
@@ -205,6 +198,13 @@ packages:
       url: "https://pub.dartlang.org"
     source: hosted
     version: "2.0.3"
+  pedantic:
+    dependency: transitive
+    description:
+      name: pedantic
+      url: "https://pub.dartlang.org"
+    source: hosted
+    version: "1.11.1"
   platform:
     dependency: transitive
     description:
@@ -265,7 +265,7 @@ packages:
       name: synchronized
       url: "https://pub.dartlang.org"
     source: hosted
-    version: "2.2.0+2"
+    version: "3.0.0"
   term_glyph:
     dependency: transitive
     description:
diff --git a/pubspec.yaml b/pubspec.yaml
index 6ce7a4a..a3acb6c 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -23,7 +23,8 @@ environment:
 dependencies:
   flutter:
     sdk: flutter
-  flame: ^0.29.4
+  flame: "^1.0.0-releasecandidate.11"
+  flame_audio: "^1.0.0-rc.1"
 
 
   # The following adds the Cupertino Icons font to your application.