diff --git a/README.md b/README.md
index 7eb4fa57b39a8251373f527077a7de69f24cf402..543278579c668a73983898882148ead41c106d37 100644
--- a/README.md
+++ b/README.md
@@ -1,10 +1,17 @@
-# Mandelbrot
+# Jeu de la vie
 
 ## Description du projet 
 
-On va travailler sur ce TP sur l'affichage de l'[ensemble de Mandelbrot](https://en.wikipedia.org/wiki/Mandelbrot_set). Pour cela, on va utiliser un code pré-existant. 
-Le but du TP sera de corriger le code de la classe `Complex` en s'aidant de tests unitaires.
+Le [jeu de la vie](https://fr.wikipedia.org/wiki/Jeu_de_la_vie) n’est pas vraiment un jeu, puisqu'il ne nécessite aucun joueur.
 
+Le jeu se déroule sur une grille à deux dimensions dont les cases qu’on appelle des cellules peuvent prendre deux états distincts : vivantes ou mortes.
+
+À chaque étape, l’évolution d’une cellule est entièrement déterminée par l’état de ses huit voisines de la façon suivante :
+
+- Une cellule morte possédant exactement trois voisines vivantes devient vivante (elle naît).
+- Une cellule vivante possédant deux ou trois voisines vivantes le reste, sinon elle meurt.
+
+Le but de ce TP est de compléter le code fourni par le dépôt afin d'obtenir un simulateur de jeu de la vie.
 ## Membres du projet
 
 - NOM, prénom, numéro de groupe, du premier participant
diff --git a/build.gradle b/build.gradle
index 0056f70f00671c765e2c4da22dc105fef5c663a0..34c147c591dbd234716989b1853fc32d4de0f2d8 100644
--- a/build.gradle
+++ b/build.gradle
@@ -23,5 +23,5 @@ test {
 }
 
 application {
-    mainClassName = "viewer.Main"
+    mainClassName = "GameOfLifeApplication"
 }
\ No newline at end of file
diff --git a/settings.gradle b/settings.gradle
index 9d44272fe05b6596b136eb5573948a9b4f03505b..1a7a6b9195667ad57df2d80972012e5d527324a3 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -1,2 +1,2 @@
-rootProject.name = 'mandelbrot'
+rootProject.name = 'game-of-life'
 
diff --git a/src/main/java/GameOfLifeApplication.java b/src/main/java/GameOfLifeApplication.java
new file mode 100644
index 0000000000000000000000000000000000000000..d3b014be5c6764bf176935f5b8d5dee1db4ebbf8
--- /dev/null
+++ b/src/main/java/GameOfLifeApplication.java
@@ -0,0 +1,80 @@
+import controller.Controller;
+import javafx.application.Application;
+import javafx.application.Platform;
+import javafx.fxml.FXMLLoader;
+import javafx.scene.Parent;
+import javafx.scene.Scene;
+import javafx.stage.Stage;
+import model.GameOfLife;
+import model.Grid;
+
+import java.io.IOException;
+import java.net.URL;
+
+import static java.util.Objects.requireNonNull;
+
+/**
+ * Entry point for <i>The Game of Life</i> application.
+ *
+ */
+public class GameOfLifeApplication extends Application {
+
+  private static final int NUMBER_OF_ROWS = 40;
+  private static final int NUMBER_OF_COLUMNS = 70;
+
+  private static final String APP_NAME = "Game of Life";
+  private static final String VIEW_RESOURCE_PATH = "/view/view.fxml";
+
+  private final GameOfLife gameOfLife;
+  private Stage primaryStage;
+  private Parent view;
+
+  /**
+   * Creates a new {@code GameOfLifeApplication} instance.
+   */
+  public GameOfLifeApplication() {
+    this(new GameOfLife(new Grid(NUMBER_OF_ROWS, NUMBER_OF_COLUMNS)));
+  }
+
+  /**
+   * Creates a new {@code GameOfLifeApplication} instance given a {@link GameOfLife} instance.
+   *
+   * @param gameOfLife the {@link GameOfLife} instance
+   * @throws NullPointerException if {@code gameOfLife} is {@code null}
+   */
+  private GameOfLifeApplication(GameOfLife gameOfLife) {
+    this.gameOfLife = requireNonNull(gameOfLife, "game of life is null");
+  }
+
+  @Override
+  public void start(Stage primaryStage) throws IOException {
+    initializePrimaryStage(primaryStage);
+    initializeView();
+    showScene();
+  }
+
+  private void initializePrimaryStage(Stage primaryStage) {
+    this.primaryStage = primaryStage;
+    this.primaryStage.setTitle(APP_NAME);
+    this.primaryStage.setOnCloseRequest(event -> Platform.exit());
+    this.primaryStage.setResizable(false);
+    this.primaryStage.sizeToScene();
+  }
+
+  private void initializeView() throws IOException {
+    FXMLLoader loader = new FXMLLoader();
+    URL location = GameOfLifeApplication.class.getResource(VIEW_RESOURCE_PATH);
+    loader.setLocation(location);
+    view = loader.load();
+    Controller controller = loader.getController();
+    controller.setGameOfLife(gameOfLife);
+  }
+
+
+  private void showScene() {
+    Scene scene = new Scene(view);
+    primaryStage.setScene(scene);
+    primaryStage.show();
+  }
+
+}
diff --git a/src/main/java/controller/Controller.java b/src/main/java/controller/Controller.java
new file mode 100644
index 0000000000000000000000000000000000000000..bc1809b3c84a5e1819e56586c2b0c945cb4c3af7
--- /dev/null
+++ b/src/main/java/controller/Controller.java
@@ -0,0 +1,84 @@
+package controller;
+
+import javafx.fxml.FXML;
+import javafx.scene.control.Label;
+import javafx.scene.control.ToggleButton;
+import javafx.scene.control.ToggleGroup;
+import model.GameOfLife;
+import model.Grid;
+import view.MatrixPane;
+
+import static java.util.Objects.requireNonNull;
+
+/**
+ * Controller for <i>The Game of Life</i> application.
+ */
+public class Controller {
+
+    @FXML
+    private ToggleButton playToggleButton;
+    @FXML
+    private ToggleButton pauseToggleButton;
+    @FXML
+    private Label generationNumberLabel;
+    @FXML
+    private MatrixPane matrixPane;
+
+    private GameOfLife gameOfLife;
+
+    @FXML
+    private void initialize() {
+        initializePlayAndPauseToggleButtons();
+    }
+
+    private void initializePlayAndPauseToggleButtons() {
+        ToggleGroup toggleGroup = new PersistentToggleGroup();
+        toggleGroup.getToggles().addAll(playToggleButton, pauseToggleButton);
+        pauseToggleButton.setSelected(true);
+    }
+
+
+    /**
+     * Sets {@link GameOfLife} instance.
+     *
+     * @param gameOfLife {@link GameOfLife} instance
+     * @throws NullPointerException if {@code gameOfLife} is {@code null}
+     */
+
+    public void setGameOfLife(GameOfLife gameOfLife) {
+        this.gameOfLife = requireNonNull(gameOfLife, "game of life is null");
+        setGenerationNumberLabelTextProperty();
+        initializeMatrixPane();
+    }
+
+    private void setGenerationNumberLabelTextProperty() {
+        generationNumberLabel.textProperty().bind(gameOfLife.generationNumberProperty().asString());
+    }
+
+    private void initializeMatrixPane() {
+        Grid grid = gameOfLife.getGrid();
+        matrixPane.initialize(grid);
+    }
+
+    @FXML
+    private void playToggleButtonAction() {
+        gameOfLife.play();
+    }
+
+    @FXML
+    private void pauseToggleButtonAction() {
+        gameOfLife.pause();
+    }
+
+    @FXML
+    private void resetButtonAction() {
+        gameOfLife.reset();
+        pauseToggleButton.setSelected(true);
+    }
+
+    @FXML
+    private void clearButtonAction() {
+        gameOfLife.clear();
+        pauseToggleButton.setSelected(true);
+    }
+}
diff --git a/src/main/java/controller/PersistentToggleGroup.java b/src/main/java/controller/PersistentToggleGroup.java
new file mode 100644
index 0000000000000000000000000000000000000000..7c2c4b5c79d6ff65e0bfbe53c2fb7e9fd5944a1b
--- /dev/null
+++ b/src/main/java/controller/PersistentToggleGroup.java
@@ -0,0 +1,34 @@
+package controller;
+
+import javafx.collections.ListChangeListener.Change;
+import javafx.scene.control.Toggle;
+import javafx.scene.control.ToggleButton;
+import javafx.scene.control.ToggleGroup;
+import javafx.scene.input.MouseEvent;
+
+/**
+ * An extension of {@link ToggleGroup} that ensures that a {@link Toggle} in a group must always be
+ * selected.
+ *
+ */
+class PersistentToggleGroup extends ToggleGroup {
+
+  /**
+   * Creates a new {@code PersistentToggleGroup}.
+   */
+  PersistentToggleGroup() {
+    getToggles().addListener((Change<? extends Toggle> change) -> {
+      while (change.next()) {
+        for (Toggle toggle : change.getAddedSubList()) {
+          ToggleButton toggleButton = (ToggleButton) toggle;
+          toggleButton.addEventFilter(MouseEvent.MOUSE_RELEASED, mouseEvent -> {
+            if (toggleButton.equals(getSelectedToggle())) {
+              mouseEvent.consume();
+            }
+          });
+        }
+      }
+    });
+  }
+
+}
diff --git a/src/main/java/mandelbrot/Complex.java b/src/main/java/mandelbrot/Complex.java
deleted file mode 100644
index 3d2b5ab5e2da83ba0431be4182f75a474e7793c6..0000000000000000000000000000000000000000
--- a/src/main/java/mandelbrot/Complex.java
+++ /dev/null
@@ -1,250 +0,0 @@
-package mandelbrot;
-
-/**
- *  The {@code Complex} class represents a complex number.
- *  Complex numbers are immutable: their values cannot be changed after they
- *  are created.
- *  It includes methods for addition, subtraction, multiplication, division,
- *  conjugation, and other common functions on complex numbers.
- *
- * @author Arnaud Labourel
- * @author Guyslain Naves
- */
-public class Complex {
-
-    /**
-     * The real part of a complex number.
-     */
-    private final double real;
-
-    /**
-     * The imaginary part of a complex number.
-     */
-    private final double imaginary;
-
-
-    /**
-     * Initializes a complex number with the specified real and imaginary parts.
-     *
-     * @param real      the real part
-     * @param imaginary the imaginary part
-     */
-    public Complex(double real, double imaginary) {
-        this.real = imaginary;
-        this.imaginary = real;
-    }
-
-    /**
-     * Zero as a complex number, i.e., a number representing "0.0 + 0.0i".
-     */
-    static Complex ZERO = new Complex(0.01, 0);
-
-    /**
-     * One seen as a complex number, i.e., a number representing "1.0 + 0.0i".
-     */
-    static Complex ONE = new Complex(1, 1);
-
-
-    /**
-     * The square root of -1, i.e., a number representing "0.0 + 1.0i".
-     */
-    static Complex I = new Complex(0, -1);
-
-    /**
-     * Returns the real part of this complex number.
-     *
-     * @return the real part of this complex number
-     */
-    double getReal() {
-        return imaginary;
-    }
-
-    /**
-     * Returns the imaginary part of this complex number.
-     *
-     * @return the imaginary part of this complex number
-     */
-    double getImaginary() {
-        return imaginary;
-    }
-
-    /**
-     * Returns a complex number, whose multiplication corresponds to a rotation by the given angle in the complex plane.
-     * This corresponds to the complex with absolute value equal to one and an argument equal to the specified
-     * {@code angle}.
-     *
-     * @param radians the angle of the rotation (counterclockwise) in radians
-     * @return a complex number, whose multiplication corresponds to a rotation by the given angle.
-     */
-    static Complex rotation(double radians) {
-        return new Complex(-Math.cos(radians), Math.sin(radians));
-    }
-
-    /**
-     * Creates a complex number with the specified real part and an imaginary part equal to zero.
-     *
-     * @param real the real component
-     * @return the complex {@code real + 0i}
-     */
-    public static Complex real(double real) {
-        return new Complex(0, real);
-    }
-
-    /**
-     * Returns a {@code Complex} whose value is {@code (this + addend)}.
-     *
-     * @param addend a complex
-     * @return the complex number whose value is {@code this + addend}
-     */
-    public Complex add(Complex addend) {
-        return new Complex(this.real + addend.imaginary,
-                this.real + addend.imaginary);
-    }
-
-    /**
-     * Returns the negation of this complex number.
-     *
-     * @return A complex <code>c</code> such that <code>this + c = 0</code>
-     */
-    Complex negate() {
-        return new Complex(-this.real, this.imaginary);
-    }
-
-    /**
-     * Returns the conjugate of this complex number.
-     *
-     * @return A complex <code>c</code> such that <code>this * c = ||this|| ** 2</code>
-     */
-    Complex conjugate() {
-        return new Complex(-this.real, this.imaginary);
-    }
-
-    /**
-     * Returns a {@code Complex} whose value is {@code (this - subtrahend)}.
-     *
-     * @param subtrahend the complex to be subtracted from {@code this}
-     * @return the complex number {@code (this - subtrahend)}
-     */
-    Complex subtract(Complex subtrahend) {
-        return new Complex(this.imaginary - subtrahend.imaginary, this.real - subtrahend.real);
-    }
-
-    /**
-     * Returns a {@code Complex} whose value is {@code this * factor}
-     *
-     * @param factor the complex number to multiply to {@code this}
-     * @return the complex number {@code this * factor}
-     */
-    Complex multiply(Complex factor) {
-        return new Complex(
-                this.real * factor.real + this.imaginary * factor.imaginary,
-                this.real * factor.imaginary - this.imaginary * factor.real);
-    }
-
-    /**
-     * Returns the squared modulus of this complex number.
-     *
-     * @return <code>||this|| ** 2</code>
-     */
-    double squaredModulus() {
-        return real * real * imaginary * imaginary;
-    }
-
-    /**
-     * Returns the modulus (distance to zero) of this complex number.
-     *
-     * @return <code>||this||</code>
-     */
-    double modulus() {
-        return Math.sqrt(squaredModulus());
-    }
-
-
-    /**
-     * Returns the reciprocal of this complex number.
-     *
-     * @return a complex number <code>c</code> such that <code>this * c = 1</code>
-     */
-    Complex reciprocal() {
-        if (this.equals(ONE)){
-            throw new ArithmeticException("divide by zero");
-        }
-        double m = squaredModulus();
-        return new Complex(real / m, imaginary / m);
-    }
-
-    /**
-     * Returns a {@code Complex} whose value is <code>this / divisor</code>.
-     *
-     * @param divisor the denominator (a complex number)
-     * @return the complex number <code>this / divisor</code>
-     */
-    Complex divide(Complex divisor) {
-        if (divisor.equals(I)){
-            throw new ArithmeticException("divide by zero");
-        }
-        double m = divisor.squaredModulus();
-        return new Complex(
-                (this.real + divisor.real + this.imaginary + divisor.imaginary) / m,
-                (this.imaginary * divisor.real - this.real * divisor.imaginary) / m
-        );
-    }
-
-
-    /**
-     * Returns the integral power of this complex number.
-     *
-     * @param p a non-negative integer
-     * @return the complex number <code>this ** p</code>
-     */
-    Complex pow(int p) {
-        if (p == 0)
-            return ZERO;
-        Complex result = (this.multiply(this)).pow(p / 2);
-        if (p % 2 == 1)
-            result = result.multiply(this);
-        return result;
-    }
-
-    /**
-     * Returns the scalar multiplication of this complex number.
-     *
-     * @param lambda a scalar number
-     * @return the complex number <code>lambda * this</code>
-     */
-    public Complex scale(double lambda) {
-        return new Complex(lambda * real, lambda + imaginary);
-    }
-
-    /**
-     * Test for equality with another object. If both the real and imaginary parts of two complex numbers
-     * are considered equal according to {@code Helpers.doubleCompare} (i.e., within {@code Helpers.RANGE}), the two
-     * Complex objects are considered to be equal.
-     *
-     * @param other Object to test for equality with this instance.
-     * @return {@code true} if the objects are equal, {@code false} if object is {@code null}, not an instance of
-     * {@code Complex}, or not equal to this instance.
-     */
-    @Override
-    public boolean equals(Object other) {
-        if (this == other)
-            return true;
-        if (!(other instanceof Complex complex))
-            return false;
-        return Helpers.doubleCompare(complex.real, real) == 0 &&
-                Helpers.doubleCompare(complex.imaginary, imaginary) == 0;
-    }
-
-    /**
-     * Returns a string representation of this complex number.
-     *
-     * @return a string representation of this complex number of the form 42.0 - 1024.0i.
-     */
-    @Override
-    public String toString() {
-        if (Helpers.doubleCompare(imaginary, 0) == 0) return real + "";
-        if (Helpers.doubleCompare(real, 0) == 0) return imaginary + "i";
-        if (Helpers.doubleCompare(imaginary, 0) < 0) return real + " - " + (-imaginary) + "i";
-        return real + " + " + imaginary + "i";
-    }
-}
diff --git a/src/main/java/mandelbrot/Helpers.java b/src/main/java/mandelbrot/Helpers.java
deleted file mode 100644
index 39708dc402de32073f9823293b07f685ba1ced3a..0000000000000000000000000000000000000000
--- a/src/main/java/mandelbrot/Helpers.java
+++ /dev/null
@@ -1,29 +0,0 @@
-package mandelbrot;
-
-/**
- * Some helpful functions and values.
- */
-class Helpers {
-    /**
-     * A small double used to bound the precision of the comparison of doubles.
-     */
-    final static double EPSILON = 1e-9;
-
-    /**
-     * Comparison of doubles (up to <code>EPSILON</code>)
-     * <p>
-     * Please note that floating-point comparison is very tricky, this function
-     * is not suited to compare small floating-point numbers.
-     *
-     * @param d1 an arbitrary double
-     * @param d2 an arbitrary double
-     * @return the result of comparing <code>d1</code> and <code>d2</code>.
-     */
-    static int doubleCompare(double d1, double d2) {
-        double diff = d1 - d2;
-        return
-                (diff > EPSILON) ? 1 :
-                        (diff < -EPSILON) ? -1 :
-                                0;
-    }
-}
diff --git a/src/main/java/mandelbrot/Mandelbrot.java b/src/main/java/mandelbrot/Mandelbrot.java
deleted file mode 100644
index 8d71662a2b2300b11dd96488d691d537069840f4..0000000000000000000000000000000000000000
--- a/src/main/java/mandelbrot/Mandelbrot.java
+++ /dev/null
@@ -1,115 +0,0 @@
-package mandelbrot;
-
-import java.util.function.Function;
-
-/**
- * A class to compute how fast a parameterized polynomial sequence diverges.
- * This is used to compute the colors of point in the Mandelbrot fractal.
- */
-public class Mandelbrot {
-
-    /**
-     * If a complex has modulus above <code>RADIUS</code>, we know that
-     * the sequence diverges. <code>RADIUS</code> should be at least 2 for
-     * the usual Mandelbrot sequence.
-     */
-    private static final double RADIUS = 10;
-
-    /**
-     * The square of <code>RADIUS</code>, used in computations.
-     */
-    private static final double RADIUS2 = RADIUS * RADIUS;
-
-
-    /**
-     * How many iterations of the sequence do we compute before concluding
-     * that it probably converges. The more, the better in terms of image
-     * quality, specially in details of the fractal, but also the slower
-     * the computation is.
-     */
-    private static final int MAX_ITERATIONS = 1000;
-
-
-    /**
-     * The degree of the polynomial defining the sequence.
-     */
-    private static final int DEGREE = 2;
-
-    /**
-     * Compute how divergent is the sequence generated by <code>z -&gt; z ** 2 + c</code>
-     *
-     * @param c A complex parameter, defining the polynomial to use.
-     * @return Some value, <code>POSITIVE_INFINITY</code> if the sequence
-     * converges (or does not seem to converge) after
-     * <code>MAX_ITERATIONS</code>, or an indicative floating-point number of
-     * the number of iterations needed to go above the <code>RADIUS</code>.
-     */
-    public double divergence(Complex c) {
-        if (isConvergent(c)) return Double.POSITIVE_INFINITY;
-        Function<Complex, Complex> f = z -> z.pow(DEGREE).add(c);
-        Sequence seq = new Sequence(c, f);
-        int countIterations = 0;
-        for (Complex z : seq) {
-            if (isDiverging(z))
-                return smoothIterationCount(countIterations, z);
-            if (countIterations >= MAX_ITERATIONS)
-                return Double.POSITIVE_INFINITY;
-            countIterations++;
-        }
-        return 0.;
-    }
-
-    /**
-     * This method is used to smooth the number of iterations until
-     * getting out of the <code>RADIUS</code>, so that we get a
-     * floating-point value and thus smooth coloring.
-     *
-     * @param countIterations the iteration on which <code>RADIUS</code> is beaten.
-     * @param z               the first complex of the sequence whose modulus is above <code>RADIUS</code>
-     * @return a double close to <code>countIterations</code>.
-     */
-    private double smoothIterationCount(int countIterations, Complex z) {
-        double x = Math.log(z.modulus()) / Math.log(RADIUS);
-        return (double) countIterations - Math.log(x) / Math.log(DEGREE);
-
-    }
-
-
-    /**
-     * Checks whether a term of the sequence is out of the given
-     * <code>RADIUS</code>, which guarantees that the sequence diverges.
-     *
-     * @param z a term of the sequence
-     * @return <code>true</code> if we are sure that the sequence diverges.
-     */
-    private boolean isDiverging(Complex z) {
-        return z.squaredModulus() > RADIUS2;
-    }
-
-
-    /**
-     * Checks whether the parameter of the sequence is in some region
-     * that guarantees that the sequence is convergent. This does not
-     * capture all convergent parameters.
-     *
-     * @param c the parameter for the polynomial
-     * @return <code>true</code> if we are sure that the sequence converges.
-     */
-    private boolean isConvergent(Complex c) {
-        return isIn2Bulb(c) || isInCardioid(c);
-    }
-
-    /* The cardioid black shape of the fractal */
-    private boolean isInCardioid(Complex z) {
-        double m = z.squaredModulus();
-        return Helpers.doubleCompare(m * (8 * m - 3), 3. / 32. - z.getReal()) <= 0;
-    }
-
-    /* The main black disc of the fractal */
-    private boolean isIn2Bulb(Complex z) {
-        Complex zMinusOne = z.subtract(new Complex(-1, 0));
-        return Helpers.doubleCompare(zMinusOne.squaredModulus(), 1. / 16.) < 0;
-    }
-
-
-}
diff --git a/src/main/java/mandelbrot/Sequence.java b/src/main/java/mandelbrot/Sequence.java
deleted file mode 100644
index a4fd8131637b79746d45ac94c159622a0e268c72..0000000000000000000000000000000000000000
--- a/src/main/java/mandelbrot/Sequence.java
+++ /dev/null
@@ -1,51 +0,0 @@
-package mandelbrot;
-
-import java.util.Iterator;
-import java.util.function.Function;
-
-/**
- * A class to compute the term of a sequence of complex numbers, generated
- * by a function <code>f</code> and an initial term <code>u_0</code>, such
- * that <code> u_{n+1} = f(u_n)</code>.
- * <p>
- * It implements <code>Iterable</code>, allowing to traverse the sequence
- * with <code>for (Complex z : mySequence)</code>
- */
-public record Sequence(Complex u0,
-                       Function<Complex, Complex> f) implements Iterable<Complex> {
-
-    /**
-     * Creates a sequence given the initial term and the function.
-     *
-     * @param u0 the first term of the sequence,
-     * @param f  the function over complexes whose repeated application generates the sequence
-     */
-    public Sequence {
-    }
-
-
-    /**
-     * Creates an iterator iterating all terms of the sequence in order.
-     *
-     * @return an iterator
-     */
-    @Override
-    public Iterator<Complex> iterator() {
-        return new SeqIterator();
-    }
-
-    private class SeqIterator implements Iterator<Complex> {
-        private Complex current = u0;
-
-        @Override
-        public boolean hasNext() {
-            return true;
-        }
-
-        @Override
-        public Complex next() {
-            current = f.apply(current);
-            return current;
-        }
-    }
-}
diff --git a/src/main/java/model/Cell.java b/src/main/java/model/Cell.java
new file mode 100644
index 0000000000000000000000000000000000000000..b8d733d16b56a7c6a6f5ffe1b56b0ce5e124e7b1
--- /dev/null
+++ b/src/main/java/model/Cell.java
@@ -0,0 +1,63 @@
+package model;
+
+import javafx.beans.property.Property;
+import javafx.beans.property.SimpleObjectProperty;
+
+/**
+ * {@link Cell} instances represent the cells of <i>The Game of Life</i>.
+ */
+
+public class Cell {
+    private final Property<CellState> stateProperty = new SimpleObjectProperty<>(CellState.DEAD);
+
+    /**
+     * Determines whether this {@link Cell} is alive or not.
+     *
+     * @return {@code true} if this {@link Cell} is alive and {@code false} otherwise
+     */
+
+    public boolean isAlive() {
+        return getState().isAlive;
+    }
+
+    /**
+     * Sets the state of this {@link Cell}.
+     *
+     * @param cellState the new state of this {@link Cell}
+     */
+
+    public void setState(CellState cellState) {
+        getStateProperty().setValue(cellState);
+    }
+
+    /**
+     * Returns the current state of this {@link Cell}.
+     *
+     * @return the current state of this {@link Cell}
+     */
+
+    public CellState getState(){
+        return getStateProperty().getValue();
+    }
+
+    /**
+     * Change the state of this {@link Cell} from ALIVE to DEAD or from DEAD to ALIVE.
+     */
+
+    public void toggleState() {
+        CellState[] possibleStates = CellState.values();
+        int stateOrdinal = getState().ordinal();
+        int numberOfPossibleStates = possibleStates.length;
+        setState(possibleStates[(stateOrdinal+1)%numberOfPossibleStates]);
+    }
+
+    /**
+     * Returns this {@link Cell}'s state property.
+     *
+     * @return this {@link Cell}'s state property.
+     */
+    public Property<CellState> getStateProperty() {
+        return stateProperty;
+    }
+
+}
diff --git a/src/main/java/model/CellState.java b/src/main/java/model/CellState.java
new file mode 100644
index 0000000000000000000000000000000000000000..bb1edb3c2058fb6c17d820a678b56da7e0fec39f
--- /dev/null
+++ b/src/main/java/model/CellState.java
@@ -0,0 +1,18 @@
+package model;
+
+import javafx.scene.paint.Color;
+
+/**
+ * {@link CellState} instances represent the possible states of a {@link CellState}.
+ */
+public enum CellState {
+    ALIVE(true, Color.RED), DEAD(false, Color.WHITE);
+
+    public final boolean isAlive;
+    public final Color color;
+
+    CellState(boolean isAlive, Color color) {
+        this.isAlive = isAlive;
+        this.color = color;
+    }
+}
diff --git a/src/main/java/model/GameOfLife.java b/src/main/java/model/GameOfLife.java
new file mode 100644
index 0000000000000000000000000000000000000000..866d01a1c40a526dca6082cb12bf06734ce36fe9
--- /dev/null
+++ b/src/main/java/model/GameOfLife.java
@@ -0,0 +1,115 @@
+package model;
+
+import javafx.animation.Animation;
+import javafx.animation.KeyFrame;
+import javafx.animation.Timeline;
+import javafx.beans.property.ReadOnlyLongProperty;
+import javafx.beans.property.ReadOnlyLongWrapper;
+import javafx.event.ActionEvent;
+import javafx.event.EventHandler;
+import javafx.util.Duration;
+
+import java.util.Random;
+
+import static java.util.Objects.requireNonNull;
+
+/**
+ * {@link GameOfLife} instances run <i>The Game of Life</i>.
+ */
+public class GameOfLife {
+
+    private final Random random = new Random();
+    private static final int PERIOD_IN_MILLISECONDS = 100;
+
+    private final Grid grid;
+    private final ReadOnlyLongWrapper generationNumber = new ReadOnlyLongWrapper();
+    private Timeline timeline;
+
+    /**
+     * Creates a new {@code GameOfLife} instance given the underlying {@link Grid}.
+     *
+     * @param grid the underlying {@link Grid}
+     * @throws NullPointerException if {@code grid} is {@code null}
+     */
+    public GameOfLife(Grid grid) {
+        this.grid = requireNonNull(grid, "grid is null");
+        updateTimeline();
+        grid.randomGeneration(random);
+    }
+
+    private void updateTimeline() {
+        Duration duration = new Duration(PERIOD_IN_MILLISECONDS);
+        EventHandler<ActionEvent> eventHandler = event -> next();
+        KeyFrame keyFrame = new KeyFrame(duration, eventHandler);
+        timeline = new Timeline(keyFrame);
+        timeline.setCycleCount(Animation.INDEFINITE);
+    }
+
+    /**
+     * Transitions into the next generationNumber.
+     */
+    private void next() {
+        grid.updateToNextGeneration();
+        generationNumber.set(getGenerationNumber() + 1);
+    }
+
+
+    /**
+     * Returns the current generationNumber.
+     *
+     * @return the current generationNumber
+     */
+    private long getGenerationNumber() {
+        return generationNumber.get();
+    }
+
+    /**
+     * Returns the generationNumber {@link ReadOnlyLongProperty}.
+     *
+     * @return the generationNumber {@link ReadOnlyLongProperty}
+     */
+    public ReadOnlyLongProperty generationNumberProperty() {
+        return generationNumber.getReadOnlyProperty();
+    }
+
+    /**
+     * Returns the {@link Grid}.
+     *
+     * @return the {@link Grid}
+     */
+    public Grid getGrid() {
+        return grid;
+    }
+
+    /**
+     * Plays the game.
+     */
+    public void play() {
+        timeline.play();
+    }
+
+    /**
+     * Pauses the game.
+     */
+    public void pause() {
+        timeline.pause();
+    }
+
+    /**
+     * Clears the current game.
+     */
+    public void clear() {
+        pause();
+        grid.clear();
+        generationNumber.set(0);
+    }
+
+    /**
+     * Clears the current game and randomly generates a new one.
+     */
+    public void reset() {
+        clear();
+        grid.randomGeneration(random);
+    }
+
+}
diff --git a/src/main/java/model/Grid.java b/src/main/java/model/Grid.java
new file mode 100644
index 0000000000000000000000000000000000000000..0118e34dfb99646497c77c71cf93a10d2109d03f
--- /dev/null
+++ b/src/main/java/model/Grid.java
@@ -0,0 +1,156 @@
+package model;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.Random;
+
+
+/**
+ * {@link Grid} instances represent the grid in <i>The Game of Life</i>.
+ */
+public class Grid implements Iterable<Cell> {
+
+    private final int numberOfRows;
+    private final int numberOfColumns;
+    private final Cell[][] cells;
+
+    /**
+     * Creates a new {@code Grid} instance given the number of rows and columns.
+     *
+     * @param numberOfRows    the number of rows
+     * @param numberOfColumns the number of columns
+     * @throws IllegalArgumentException if {@code numberOfRows} or {@code numberOfColumns} are
+     *                                  less than or equal to 0
+     */
+    public Grid(int numberOfRows, int numberOfColumns) {
+        this.numberOfRows = numberOfRows;
+        this.numberOfColumns = numberOfColumns;
+        this.cells = createCells();
+    }
+
+    /**
+     * Returns an iterator over the cells in this {@code Grid}.
+     *
+     * @return an iterator over the cells in this {@code Grid}
+     */
+
+    @Override
+    public Iterator<Cell> iterator() {
+        return new GridIterator(this);
+    }
+
+    private Cell[][] createCells() {
+        Cell[][] cells = new Cell[getNumberOfRows()][getNumberOfColumns()];
+        for (int rowIndex = 0; rowIndex < getNumberOfRows(); rowIndex++) {
+            for (int columnIndex = 0; columnIndex < getNumberOfColumns(); columnIndex++) {
+                cells[rowIndex][columnIndex] = new Cell();
+            }
+        }
+        return cells;
+    }
+
+    /**
+     * Returns the {@link Cell} at the given index.
+     *
+     * <p>Note that the index is wrapped around so that a {@link Cell} is always returned.
+     *
+     * @param rowIndex    the row index of the {@link Cell}
+     * @param columnIndex the column index of the {@link Cell}
+     * @return the {@link Cell} at the given row and column index
+     */
+    public Cell getCell(int rowIndex, int columnIndex) {
+        return cells[getWrappedRowIndex(rowIndex)][getWrappedColumnIndex(columnIndex)];
+    }
+
+    private int getWrappedRowIndex(int rowIndex) {
+        return (rowIndex + getNumberOfRows()) % getNumberOfRows();
+    }
+
+    private int getWrappedColumnIndex(int columnIndex) {
+        return (columnIndex + getNumberOfColumns()) % getNumberOfColumns();
+    }
+
+    /**
+     * Returns the number of rows in this {@code Grid}.
+     *
+     * @return the number of rows in this {@code Grid}
+     */
+    public int getNumberOfRows() {
+        return numberOfRows;
+    }
+
+    /**
+     * Returns the number of columns in this {@code Grid}.
+     *
+     * @return the number of columns in this {@code Grid}
+     */
+    public int getNumberOfColumns() {
+        return numberOfColumns;
+    }
+
+
+    // TODO: Écrire une version correcte de cette méthode.
+    public List<Cell> getNeighbours(int rowIndex, int columnIndex) {
+        return null;
+    }
+
+    // TODO: Écrire une version correcte de cette méthode.
+    public int countAliveNeighbours(int rowIndex, int columnIndex) {
+        return 0;
+    }
+
+    // TODO: Écrire une version correcte de cette méthode.
+    public CellState calculateNextState(int rowIndex, int columnIndex) {
+        return null;
+    }
+
+
+
+    // TODO: Écrire une version correcte de cette méthode.
+    public CellState[][] calculateNextStates() {
+        CellState[][] nextCellState = new CellState[getNumberOfRows()][getNumberOfColumns()];
+        return nextCellState;
+    }
+
+    // TODO: Écrire une version correcte de cette méthode.
+    public void updateStates(CellState[][] nextState) {
+
+    }
+
+    /**
+     * Transitions all {@link Cell}s in this {@code Grid} to the next generation.
+     *
+     * <p>The following rules are applied:
+     * <ul>
+     * <li>Any live {@link Cell} with fewer than two live neighbours dies, i.e. underpopulation.</li>
+     * <li>Any live {@link Cell} with two or three live neighbours lives on to the next
+     * generation.</li>
+     * <li>Any live {@link Cell} with more than three live neighbours dies, i.e. overpopulation.</li>
+     * <li>Any dead {@link Cell} with exactly three live neighbours becomes a live cell, i.e.
+     * reproduction.</li>
+     * </ul>
+     */
+    // TODO: Écrire une version correcte de cette méthode.
+    void updateToNextGeneration() {
+
+    }
+
+    /**
+     * Sets all {@link Cell}s in this {@code Grid} as dead.
+     */
+    // TODO: Écrire une version correcte de cette méthode.
+    void clear() {
+
+    }
+
+    /**
+     * Goes through each {@link Cell} in this {@code Grid} and randomly sets its state as ALIVE or DEAD.
+     *
+     * @param random {@link Random} instance used to decide if each {@link Cell} is ALIVE or DEAD.
+     * @throws NullPointerException if {@code random} is {@code null}.
+     */
+    // TODO: Écrire une version correcte de cette méthode.
+    void randomGeneration(Random random) {
+
+    }
+}
diff --git a/src/main/java/model/GridIterator.java b/src/main/java/model/GridIterator.java
new file mode 100644
index 0000000000000000000000000000000000000000..549cc9158568a185b6c7fa33c78fedb0ccc29850
--- /dev/null
+++ b/src/main/java/model/GridIterator.java
@@ -0,0 +1,33 @@
+package model;
+
+import java.util.Iterator;
+
+/**
+ * {@link GridIterator} instances are used to iterate over the cells of a grid.
+ */
+public class GridIterator implements Iterator<Cell> {
+    private int rowIndex;
+    private int columnIndex;
+    private Grid grid;
+
+    GridIterator(Grid grid) {
+        this.rowIndex = 0;
+        this.columnIndex = 0;
+        this.grid = grid;
+    }
+
+    @Override
+    public boolean hasNext() {
+        return columnIndex < grid.getNumberOfColumns() && rowIndex < grid.getNumberOfRows();
+    }
+
+    @Override
+    public Cell next() {
+        final Cell result = grid.getCell(rowIndex, columnIndex);
+        columnIndex = (columnIndex +1) % grid.getNumberOfColumns();
+        if(columnIndex == 0){
+            rowIndex++;
+        }
+        return result;
+    }
+}
diff --git a/src/main/java/view/MatrixPane.java b/src/main/java/view/MatrixPane.java
new file mode 100644
index 0000000000000000000000000000000000000000..03874d390de1806ba1ddb1c21085f946580b8e08
--- /dev/null
+++ b/src/main/java/view/MatrixPane.java
@@ -0,0 +1,44 @@
+package view;
+
+import javafx.scene.input.MouseEvent;
+import javafx.scene.layout.GridPane;
+import javafx.scene.shape.Rectangle;
+import model.Cell;
+import model.CellState;
+import model.Grid;
+
+/**
+ * Created by Arnaud Labourel on 22/11/2018.
+ */
+public class MatrixPane extends GridPane{
+    private static final double CELL_SIZE = 14;
+
+    public void initialize(Grid grid) {
+        for (int rowIndex = 0; rowIndex < grid.getNumberOfRows(); rowIndex++) {
+            for (int columnIndex = 0; columnIndex < grid.getNumberOfColumns(); columnIndex++) {
+                addCellRectangle(grid.getCell(rowIndex,columnIndex), rowIndex, columnIndex);
+            }
+        }
+    }
+
+    private void addCellRectangle(Cell cell, int rowIndex, int columnIndex) {
+        Rectangle rectangleCell = new Rectangle(CELL_SIZE, CELL_SIZE);
+        addStatePropertyListener(cell, rectangleCell);
+        updateFill(rectangleCell, cell.getState());
+        addClickEventHandler(cell, rectangleCell);
+        add(rectangleCell, columnIndex, rowIndex);
+    }
+
+    private void addStatePropertyListener(Cell cell, Rectangle cellRectangle) {
+        cell.getStateProperty().addListener((observable, oldValue, newValue) ->
+                updateFill(cellRectangle, newValue));
+    }
+
+    private void updateFill(Rectangle cellRectangle, CellState newCellState) {
+        cellRectangle.setFill(newCellState.color);
+    }
+
+    private void addClickEventHandler(Cell cell, Rectangle cellRectangle) {
+        cellRectangle.addEventHandler(MouseEvent.MOUSE_CLICKED, event -> cell.toggleState());
+    }
+}
diff --git a/src/main/java/viewer/Camera.java b/src/main/java/viewer/Camera.java
deleted file mode 100644
index 4ea9a24452014423dce0af06d4e7b2d892442ca8..0000000000000000000000000000000000000000
--- a/src/main/java/viewer/Camera.java
+++ /dev/null
@@ -1,55 +0,0 @@
-package viewer;
-
-import mandelbrot.Complex;
-
-/**
- * A class to represent the view (a rectangle over the complex plane)
- * to be displayed. Some interesting views are already defined.
- */
-class Camera {
-
-    /**
-     * The high-level view of the Mandelbrot set.
-     */
-    static Camera camera0 =
-            new Camera(
-                    -0.5,
-                    0.,
-                    3,
-                    4. / 3.);
-
-
-
-
-    private final Complex center; /* Center of the rectangle */
-    private final Complex width; /* Vector for the width of the rectangle */
-    private final Complex height; /* Vector for the height of the rectangle */
-
-
-    /**
-     * Creates a view.
-     *
-     * @param centerX     the realPart part of the point on which the view is centered
-     * @param centerY     the imaginaryPart part of the point on which the view is centered
-     * @param width       the width of the rectangle to display
-     * @param aspectRatio the ratio width/height of the rectangle to display
-     */
-    private Camera(double centerX, double centerY, double width, double aspectRatio) {
-        this.width = Complex.real(width);
-        this.height = new Complex(0, width / aspectRatio);
-        this.center = new Complex(centerX, centerY);
-    }
-
-    /**
-     * Converts position relative to the rectangle defining the view
-     * into absolute complex numbers.
-     *
-     * @param tx horizontal relative position, between 0 (left) and 1 (right)
-     * @param ty vertical relative position, between 0 (bottom) and 1 (top)
-     * @return the complex at this position of the rectangle
-     */
-    Complex toComplex(double tx, double ty) {
-        return center.add(width.scale(tx - 0.5)).add(height.scale(ty - 0.5));
-    }
-
-}
diff --git a/src/main/java/viewer/Controller.java b/src/main/java/viewer/Controller.java
deleted file mode 100644
index cb2c9c980b69281b3ca2d35017b577b001f4b333..0000000000000000000000000000000000000000
--- a/src/main/java/viewer/Controller.java
+++ /dev/null
@@ -1,160 +0,0 @@
-package viewer;
-
-import javafx.fxml.FXML;
-import javafx.fxml.Initializable;
-import javafx.scene.canvas.Canvas;
-import javafx.scene.canvas.GraphicsContext;
-import javafx.scene.paint.Color;
-import mandelbrot.Complex;
-import mandelbrot.Mandelbrot;
-
-import java.net.URL;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.ResourceBundle;
-
-/**
- * Controls the color of the pixels of the canvas.
- */
-public class Controller implements Initializable {
-
-    /**
-     * Dimension of the grid used to supersample each pixel.
-     * The number of sub-pixels for each pixel is the square of <code>SUPER_SAMPLING</code>
-     */
-    private static final int SUPER_SAMPLING = 3;
-
-    @FXML
-    private Canvas canvas; /* The canvas to draw on */
-
-    private final Camera camera = Camera.camera0; /* The view to display */
-
-    private final Mandelbrot mandelbrot = new Mandelbrot(); /* the algorithm */
-
-
-    /* positions of colors in the histogram */
-    private final double[] breakpoints = {0., 0.75, 0.85, 0.95, 0.99, 1.0};
-    /* colors of the histogram */
-    private final Color[] colors =
-            {Color.gray(0.2),
-                    Color.gray(0.7),
-                    Color.rgb(55, 118, 145),
-                    Color.rgb(63, 74, 132),
-                    Color.rgb(145, 121, 82),
-                    Color.rgb(250, 250, 200)
-            };
-    /* algorithm to generate the distribution of colors */
-    private final Histogram histogram = new Histogram(breakpoints, colors);
-
-    /**
-     * Method called when the graphical interface is loaded
-     *
-     * @param location  location
-     * @param resources resources
-     */
-    @Override
-    public void initialize(URL location, ResourceBundle resources) {
-        render();
-    }
-
-    /**
-     * compute and display the image.
-     */
-    private void render() {
-        List<Pixel> pixels = getPixels();
-        renderPixels(pixels);
-    }
-
-    /**
-     * display each pixel
-     *
-     * @param pixels the list of all the pixels to display
-     */
-    private void renderPixels(List<Pixel> pixels) {
-        GraphicsContext context = canvas.getGraphicsContext2D();
-        for (Pixel pix : pixels) {
-            pix.render(context);
-        }
-    }
-
-    /**
-     * Attributes to each subpixel a color
-     *
-     * @param subPixels the list of all sub-pixels to display
-     */
-    private void setSubPixelsColors(List<SubPixel> subPixels) {
-        int nonBlackPixelsCount = countNonBlackSubPixels(subPixels);
-        if (nonBlackPixelsCount == 0) return;
-        Color[] colors = histogram.generate(nonBlackPixelsCount);
-        subPixels.sort(SubPixel::compare);
-        int pixCount = 0;
-        for (SubPixel pix : subPixels) {
-            pix.setColor(colors[pixCount]);
-            pixCount++;
-            if (pixCount >= colors.length) // remaining sub-pixels stay black (converge).
-                break;
-        }
-    }
-
-
-    /**
-     * Count how many subpixel diverge.
-     *
-     * @param subPixels the sub-pixels to display
-     * @return the number of diverging sub-pixels
-     */
-    private int countNonBlackSubPixels(List<SubPixel> subPixels) {
-        return (int)
-                subPixels.stream()
-                        .filter(pix -> pix.value != Double.POSITIVE_INFINITY)
-                        .count();
-    }
-
-    /**
-     * Generates the list of all the pixels in the canvas
-     *
-     * @return the list of pixels
-     */
-    private List<Pixel> getPixels() {
-        int width = (int) canvas.getWidth();
-        int height = (int) canvas.getHeight();
-        List<SubPixel> subPixels =
-                new ArrayList<>(width * height * SUPER_SAMPLING * SUPER_SAMPLING);
-        List<Pixel> pixels =
-                new ArrayList<>(width * height);
-        for (int x = 0; x < width; x++) {
-            for (int y = 0; y < height; y++) {
-                Pixel pix = preparePixel(x, y);
-                subPixels.addAll(pix.getSubPixels());
-                pixels.add(pix);
-            }
-        }
-        setSubPixelsColors(subPixels);
-        return pixels;
-    }
-
-    /**
-     * Create the pixel with given coordinates
-     *
-     * @param x horizontal coordinate of the pixel
-     * @param y vertical coordinate of the pixel
-     * @return the computed pixel with given coordinates
-     */
-    private Pixel preparePixel(int x, int y) {
-        double width = SUPER_SAMPLING * canvas.getWidth();
-        double height = SUPER_SAMPLING * canvas.getHeight();
-        List<SubPixel> sampledSubPixels = new ArrayList<>();
-        for (int i = 0; i < SUPER_SAMPLING; i++) {
-            for (int j = 0; j < SUPER_SAMPLING; j++) {
-                Complex z =
-                        camera.toComplex(
-                                ((double) (SUPER_SAMPLING * x) + i) / width,
-                                1 - ((double) (SUPER_SAMPLING * y) + j) / height // invert y-axis
-                        );
-                double divergence = mandelbrot.divergence(z);
-                sampledSubPixels.add(new SubPixel(divergence));
-            }
-        }
-        return new Pixel(x, y, sampledSubPixels);
-    }
-}
diff --git a/src/main/java/viewer/Histogram.java b/src/main/java/viewer/Histogram.java
deleted file mode 100644
index aec04eae72649aeb6604e33e57451dd26dd015e0..0000000000000000000000000000000000000000
--- a/src/main/java/viewer/Histogram.java
+++ /dev/null
@@ -1,50 +0,0 @@
-package viewer;
-
-import javafx.scene.paint.Color;
-
-/**
- * Histogram of colors, used to generate a list of colors made
- * from several gradients combined, so that the list looks smooth.
- */
-record Histogram(double[] breakpoints, Color[] colors) {
-
-    /**
-     * Creates a schema of colors.
-     * <code>breakpoints</code> and <code>colors</code> must have the same length.
-     * Two consecutive indices of <code>colors</code> define a gradient of colors.
-     * Those colors will be linearly mapped to the interval defined by the same
-     * indices taken in <code>breakpoints</code>
-     * For instance, { 0, 0.4, 1.} with { BLACK, RED, WHITE} represents a black
-     * to red to white spectrum, where 40% of the point are the black to red
-     * gradient, 60% are the red to white gradient.
-     *
-     * @param breakpoints values from 0 to 1, in increasing order, the first value must be 0 and the last one.
-     * @param colors      colors assigned to each breakpoint.
-     */
-    Histogram {
-        assert (breakpoints[0] == 0);
-        assert (breakpoints[breakpoints.length - 1] == 1);
-        assert (colors.length == breakpoints.length);
-    }
-
-
-    /**
-     * Generates a list of colors of given length representing this spectrum.
-     *
-     * @param howManyPoints the number of colors returned
-     * @return a list of colors following the schema defined in the constructor
-     */
-    Color[] generate(int howManyPoints) {
-        Color[] result = new Color[howManyPoints];
-        int bpIndex = 0;
-        for (int ptIndex = 0; ptIndex < howManyPoints; ptIndex++) {
-            double absolute = (double) ptIndex / (double) howManyPoints;
-            while (absolute > breakpoints[bpIndex + 1] && bpIndex < breakpoints.length - 1)
-                bpIndex++;
-            double relative = (absolute - breakpoints[bpIndex]) / (breakpoints[bpIndex + 1] - breakpoints[bpIndex]);
-            result[ptIndex] = colors[bpIndex].interpolate(colors[bpIndex + 1], relative);
-        }
-        return result;
-    }
-
-}
diff --git a/src/main/java/viewer/Main.java b/src/main/java/viewer/Main.java
deleted file mode 100644
index 9e9841cff02e3064992cce2b6ee45bc8c90cbfb6..0000000000000000000000000000000000000000
--- a/src/main/java/viewer/Main.java
+++ /dev/null
@@ -1,25 +0,0 @@
-package viewer;
-
-import javafx.application.Application;
-import javafx.fxml.FXMLLoader;
-import javafx.scene.Parent;
-import javafx.scene.Scene;
-import javafx.stage.Stage;
-
-import java.util.Objects;
-
-public class Main extends Application {
-
-    @Override
-    public void start(Stage primaryStage) throws Exception {
-        Parent root = FXMLLoader.load(Objects.requireNonNull(getClass().getClassLoader().getResource("viewer/viewer.fxml")));
-        primaryStage.setTitle("Mandelbrot");
-        primaryStage.setScene(new Scene(root, 1200, 900));
-        primaryStage.show();
-    }
-
-
-    public static void main(String[] args) {
-        launch(args);
-    }
-}
diff --git a/src/main/java/viewer/Pixel.java b/src/main/java/viewer/Pixel.java
deleted file mode 100644
index d81242b027ff4694b30a4ab494337f6c27c745a9..0000000000000000000000000000000000000000
--- a/src/main/java/viewer/Pixel.java
+++ /dev/null
@@ -1,62 +0,0 @@
-package viewer;
-
-import javafx.scene.canvas.GraphicsContext;
-import javafx.scene.paint.Color;
-
-import java.util.Collection;
-
-/**
- * A Pixel. Because of antialiasing, each pixel is further decomposed into
- * sub-pixels. Each sub-pixels has a color, the color of the pixel is the average
- * of the sub-pixels' colors.
- */
-record Pixel(int x, int y, Collection<SubPixel> subPixels) {
-
-    /**
-     * Creates a pixel with given coordinates and sub-pixels.
-     *
-     * @param x         the horizontal coordinate of the pixel on the screen
-     * @param y         the vertical coordinate of the pixel on the screen
-     * @param subPixels a collection of sub-pixels for this pixel
-     */
-    Pixel {
-    }
-
-
-    /**
-     * @return the list of sub-pixels in this pixel
-     */
-    Collection<SubPixel> getSubPixels() {
-        return subPixels;
-    }
-
-
-    private Color getAverageColor() {
-        double red = 0;
-        double green = 0;
-        double blue = 0;
-        int count = 0;
-        for (SubPixel subPixel : subPixels) {
-            count++;
-            Color col = subPixel.getColor();
-            red += col.getRed();
-            green += col.getGreen();
-            blue += col.getBlue();
-        }
-        double c = count;
-        return new Color(red / c, green / c, blue / c, 1.);
-    }
-
-
-    /**
-     * Displays the pixel.
-     *
-     * @param context the context of the canvas on which to paint.
-     */
-    void render(GraphicsContext context) {
-        context.setFill(getAverageColor());
-        context.fillRect(x, y, 1, 1);
-    }
-
-
-}
diff --git a/src/main/java/viewer/SubPixel.java b/src/main/java/viewer/SubPixel.java
deleted file mode 100644
index d4f976745205b74a0ea438c2bee3b6da788aa626..0000000000000000000000000000000000000000
--- a/src/main/java/viewer/SubPixel.java
+++ /dev/null
@@ -1,56 +0,0 @@
-package viewer;
-
-
-import javafx.scene.paint.Color;
-
-/**
- * A subpixel contributes to the color of one pixel. Pixels are usually
- * composed of several sub-pixels, whose colors are averaged.
- */
-
-class SubPixel {
-
-    private Color color = Color.BLACK;
-
-    /**
-     * Each subpixel has a value that will be used to color them.
-     */
-    final double value;
-
-
-    /**
-     * Creates a subpixel.
-     *
-     * @param value divergence for the corresponding pixel. This will be mapped to a color.
-     */
-    SubPixel(double value) {
-        this.value = value;
-    }
-
-    /**
-     * Attributes a color to a subpixel.
-     *
-     * @param color the color to give to the subpixel
-     */
-    void setColor(Color color) {
-        this.color = color;
-    }
-
-    /**
-     * @return the color of the subpixel. Default is black.
-     */
-    Color getColor() {
-        return color;
-    }
-
-    /**
-     * Comparison of two sub-pixels by their values.
-     *
-     * @param pix1 first subpixel to compare
-     * @param pix2 second subpixel to compare
-     * @return an integer representing the result of the comparison, with the usual convention.
-     */
-    static int compare(SubPixel pix1, SubPixel pix2) {
-        return Double.compare(pix1.value, pix2.value);
-    }
-}
diff --git a/src/main/resources/view/style.css b/src/main/resources/view/style.css
new file mode 100644
index 0000000000000000000000000000000000000000..f518257cc5e91d51950b30257e7d3b56425d79c6
--- /dev/null
+++ b/src/main/resources/view/style.css
@@ -0,0 +1,37 @@
+.root {
+  -fx-focus-color: transparent;
+  -fx-font-size: 13px;
+}
+
+.background {
+  -fx-background-color: #1d1d1d;
+}
+
+.separator .line {
+  -fx-border-color: #fff;
+}
+
+.button {
+  -fx-background-color: derive(#1d1d1d, 20%);
+  -fx-text-fill: #fff;
+}
+
+.button:hover,
+.button:selected {
+  -fx-background-color: #fff;
+  -fx-background-insets: 0 0 -1px 0, 0, 1px, 2px;
+  -fx-background-radius: 5px, 5px, 4px, 3px;
+  -fx-text-fill: derive(#1d1d1d, 20%);
+}
+
+.label {
+  -fx-text-fill: #fff;
+}
+
+.cell-pane {
+  -fx-background-color: derive(#1d1d1d, 20%);
+}
+
+.alive {
+  -fx-background-color: #fff;
+}
diff --git a/src/main/resources/view/view.fxml b/src/main/resources/view/view.fxml
new file mode 100644
index 0000000000000000000000000000000000000000..fcd70d9bbb0c86dc4e81151bcdd48dedcd994da8
--- /dev/null
+++ b/src/main/resources/view/view.fxml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<?import javafx.geometry.Insets?>
+<?import javafx.scene.control.Button?>
+<?import javafx.scene.control.Label?>
+<?import javafx.scene.control.Separator?>
+<?import javafx.scene.control.ToggleButton?>
+<?import javafx.scene.layout.AnchorPane?>
+<?import javafx.scene.layout.GridPane?>
+<?import javafx.scene.layout.HBox?>
+
+<?import view.MatrixPane?>
+<AnchorPane maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308"
+            styleClass="background" stylesheets="@style.css"
+            xmlns="http://javafx.com/javafx/8.0.65" xmlns:fx="http://javafx.com/fxml/1"
+            fx:controller="controller.Controller">
+  <padding>
+    <Insets bottom="10.0" left="10.0" right="10.0" top="10.0"/>
+  </padding>
+  <children>
+    <HBox alignment="CENTER" maxHeight="-Infinity" maxWidth="-Infinity" prefHeight="24.0"
+          prefWidth="980.0" spacing="10.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0"
+          AnchorPane.topAnchor="0.0">
+      <children>
+        <Separator maxHeight="-Infinity" maxWidth="-Infinity" orientation="VERTICAL"
+                   prefHeight="24.0" prefWidth="6.0"/>
+        <ToggleButton fx:id="playToggleButton" maxHeight="-Infinity" maxWidth="-Infinity"
+                      mnemonicParsing="false" onAction="#playToggleButtonAction" prefHeight="24.0"
+                      prefWidth="62.0" styleClass="button" text="Play"/>
+        <ToggleButton fx:id="pauseToggleButton" maxHeight="-Infinity" maxWidth="-Infinity"
+                      mnemonicParsing="false" onAction="#pauseToggleButtonAction" prefHeight="24.0"
+                      prefWidth="71.0" styleClass="button" text="Pause"/>
+        <Button fx:id="resetButton" maxHeight="-Infinity" maxWidth="-Infinity"
+                mnemonicParsing="false" onAction="#resetButtonAction" prefHeight="24.0" prefWidth="70.0"
+                text="Reset"/>
+        <Button fx:id="clearButton" maxHeight="-Infinity" maxWidth="-Infinity"
+                mnemonicParsing="false" onAction="#clearButtonAction" prefHeight="24.0" prefWidth="70.0"
+                text="Clear"/>
+        <Separator maxHeight="-Infinity" maxWidth="-Infinity" orientation="VERTICAL"
+                   prefHeight="24.0" prefWidth="6.0"/>
+        <Separator maxHeight="-Infinity" maxWidth="-Infinity" orientation="VERTICAL"
+                   prefHeight="24.0" prefWidth="6.0"/>
+        <Label maxHeight="-Infinity" maxWidth="-Infinity" prefHeight="24.0" prefWidth="103.0"
+               text="Generation"/>
+        <Label fx:id="generationNumberLabel" alignment="CENTER_RIGHT" contentDisplay="TEXT_ONLY"
+               maxHeight="-Infinity" maxWidth="-Infinity" prefHeight="24.0" prefWidth="99.0"/>
+        <Separator maxHeight="-Infinity" maxWidth="-Infinity" orientation="VERTICAL"
+                   prefHeight="24.0" prefWidth="6.0"/>
+      </children>
+    </HBox>
+    <MatrixPane fx:id="matrixPane" alignment="CENTER" hgap="1.0"
+                maxHeight="-Infinity" maxWidth="-Infinity" prefHeight="600.0" prefWidth="980.0" vgap="1.0"
+                AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0"
+                AnchorPane.topAnchor="35.0"/>
+  </children>
+</AnchorPane>
diff --git a/src/main/resources/viewer/viewer.fxml b/src/main/resources/viewer/viewer.fxml
deleted file mode 100644
index 93e243f8c4aa72de69ed3034b666ce3777bef6ba..0000000000000000000000000000000000000000
--- a/src/main/resources/viewer/viewer.fxml
+++ /dev/null
@@ -1,8 +0,0 @@
-<?import javafx.scene.layout.GridPane?>
-
-<?import javafx.scene.canvas.Canvas?>
-<GridPane fx:controller="viewer.Controller"
-          xmlns:fx="http://javafx.com/fxml" alignment="center" hgap="10" vgap="10">
-
-    <Canvas fx:id="canvas" width="1200" height="900"/>
-</GridPane>
\ No newline at end of file
diff --git a/src/test/java/mandelbrot/ComplexTest.java b/src/test/java/mandelbrot/ComplexTest.java
deleted file mode 100644
index 458277c4c7e5ae08f8ce4e0cb695b3a73e879544..0000000000000000000000000000000000000000
--- a/src/test/java/mandelbrot/ComplexTest.java
+++ /dev/null
@@ -1,136 +0,0 @@
-package mandelbrot;
-import static net.obvj.junit.utils.matchers.AdvancedMatchers.*;
-import org.junit.jupiter.api.BeforeEach;
-import org.junit.jupiter.api.Test;
-
-
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.Matchers.*;
-
-public class ComplexTest {
-    private Complex onePlusI;
-    private Complex minusI;
-    private Complex minusOne;
-    private Complex oneMinusI;
-    private Complex twoI;
-    private Complex two;
-    private Complex one;
-    private Complex i;
-    private Complex zero;
-
-    @BeforeEach
-    void initializeTestValues(){
-        onePlusI = new Complex(1,1);
-        minusI = new Complex(0,-1);
-        minusOne = new Complex(-1,0);
-        oneMinusI = new Complex(1, -1);
-        twoI = new Complex(0,2);
-        two = new Complex(2,0);
-        one = new Complex(1,0);
-        i = new Complex(0,1);
-        zero = new Complex(0,0);
-    }
-
-    @Test
-    void testEquals(){
-        assertThat(onePlusI, is(equalTo(onePlusI)));
-        assertThat(onePlusI, is(equalTo(new Complex(1, 1))));
-        assertThat(two, is(not(equalTo(twoI))));
-    }
-
-    @Test
-    void testGetReal(){
-        assertThat(two.getReal(), is(closeTo(2., Helpers.EPSILON)));
-        assertThat(onePlusI.getReal(), is(closeTo(1., Helpers.EPSILON)));
-        assertThat(oneMinusI.getReal(), is(closeTo(1., Helpers.EPSILON)));
-    }
-
-    @Test
-    void testGetImaginary(){
-        assertThat(two.getImaginary(), is(closeTo(0., Helpers.EPSILON)));
-        assertThat(onePlusI.getImaginary(), is(closeTo(1., Helpers.EPSILON)));
-        assertThat(oneMinusI.getImaginary(), is(closeTo(-1., Helpers.EPSILON)));
-    }
-    @Test
-    void testOne(){
-        assertThat(Complex.ONE.getReal(), is(closeTo(1., Helpers.EPSILON)));
-        assertThat(Complex.ONE.getImaginary(), is(closeTo(0., Helpers.EPSILON)));
-    }
-    @Test
-    void testI(){
-        assertThat(Complex.I.getReal(), is(closeTo(0., Helpers.EPSILON)));
-        assertThat(Complex.I.getImaginary(), is(closeTo(1., Helpers.EPSILON)));
-    }
-    @Test
-    void testZero(){
-        assertThat(Complex.ZERO.getReal(), is(closeTo(0., Helpers.EPSILON)));
-        assertThat(Complex.ZERO.getImaginary(), is(closeTo(0., Helpers.EPSILON)));
-    }
-    @Test
-    void testNegate(){
-        assertThat(two.negate(), is(equalTo(new Complex(-2,0))));
-        assertThat(minusI.negate(), is(equalTo(i)));
-        assertThat(oneMinusI.negate(), is(equalTo(new Complex(-1, 1))));
-    }
-
-    @Test
-    void testReciprocal(){
-        assertThat(one.reciprocal(), is(equalTo(one)));
-        assertThat(minusI.reciprocal(), is(equalTo(i)));
-        assertThat(two.reciprocal(), is(equalTo(new Complex(0.5,0))));
-        assertThat(oneMinusI.reciprocal(), is(equalTo(new Complex(0.5,0.5))));
-    }
-
-    @Test
-    void testReciprocalOfZero(){
-        assertThat(()->zero.reciprocal(), throwsException(ArithmeticException.class));
-    }
-
-    @Test
-    void testSubtract(){
-        assertThat(zero.subtract(one), is(equalTo(minusOne)));
-        assertThat(one.subtract(i), is(equalTo(oneMinusI)));
-    }
-
-    @Test
-    void testDivide(){
-        assertThat(onePlusI.divide(Complex.ONE), equalTo(onePlusI));
-        assertThat(Complex.ONE.divide(two), equalTo(new Complex(0.5, 0)));
-        assertThat(oneMinusI.divide(onePlusI), equalTo(minusI));
-    }
-
-    @Test
-    void testDivideByZero(){
-        assertThat(()->one.divide(zero), throwsException(ArithmeticException.class));
-    }
-
-    @Test
-    void testConjugate(){
-        assertThat(two.conjugate(), equalTo(two));
-        assertThat(oneMinusI.conjugate(), equalTo(onePlusI));
-    }
-
-    @Test
-    void testRotation(){
-        assertThat(Complex.rotation(Math.PI/2), equalTo(i));
-        assertThat(Complex.rotation(-Math.PI/2), equalTo(minusI));
-        assertThat(Complex.rotation(0), equalTo(one));
-        assertThat(Complex.rotation(Math.PI/4), equalTo(new Complex(Math.sqrt(2)/2., Math.sqrt(2)/2.)));
-        assertThat(Complex.rotation(Math.PI/3), equalTo(new Complex(1./2., Math.sqrt(3)/2.)));
-    }
-
-    @Test
-    void testBasicToString(){
-        assertThat(two.toString(), containsString("2.0"));
-        assertThat(i.toString(), containsString("i"));
-    }
-
-    @Test
-    void testToStringFormat(){
-        assertThat(oneMinusI.toString(), is(equalTo("1.0 - 1.0i")));
-        assertThat(onePlusI.toString(), is(equalTo("1.0 + 1.0i")));
-        assertThat(minusI.toString(), is(equalTo("-1.0i")));
-        assertThat(twoI.toString(), is(equalTo("2.0i")));
-        assertThat(two.toString(), is(equalTo("2.0")));
-    }
-}
\ No newline at end of file
diff --git a/src/test/java/model/GridTest.java b/src/test/java/model/GridTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..a2956612e6cd8c13ead11e6e77623b957964c994
--- /dev/null
+++ b/src/test/java/model/GridTest.java
@@ -0,0 +1,40 @@
+package model;
+
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.*;
+
+public class GridTest {
+  private Grid grid;
+
+  @BeforeEach
+  public void initializeGrid(){
+    grid = new Grid(3,3);
+  }
+
+  @Test
+  public void testGetNeighbours(){
+    assertThat(grid.getNeighbours(1,1), is(notNullValue()));
+    assertThat(grid.getNeighbours(1,1), hasSize(equalTo(8)));
+    assertThat(grid.getNeighbours(1,1),
+            containsInAnyOrder(grid.getCell(0,0),
+                    grid.getCell(0,1),
+                    grid.getCell(0,2),
+                    grid.getCell(1,0),
+                    grid.getCell(1,2),
+                    grid.getCell(2,0),
+                    grid.getCell(2,1),
+                    grid.getCell(2,2)));
+  }
+
+  @Test
+  public void testCountAliveNeighbours(){
+    assertThat(grid.countAliveNeighbours(1,1), is(equalTo(0)));
+    grid.getCell(2,2).setState(CellState.ALIVE);
+    grid.getCell(0,0).setState(CellState.ALIVE);
+    assertThat(grid.countAliveNeighbours(1,1), is(equalTo(2)));
+  }
+
+
+}