diff --git a/pom.xml b/pom.xml
index 53a059ee66a70ef50dbbb866e904112f2ae64eb3..31f71f2f89db024293be17c164419ce3b1564efe 100644
--- a/pom.xml
+++ b/pom.xml
@@ -3,7 +3,7 @@
 	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
 	<modelVersion>4.0.0</modelVersion>
 	<groupId>vsb-cs-java1</groupId>
-	<artifactId>lab02</artifactId>
+	<artifactId>lab03</artifactId>
 	<version>0.0.1-SNAPHOST</version>
 	<packaging>jar</packaging>
 	<properties>
diff --git a/src/main/java/lab/App.java b/src/main/java/lab/App.java
index ce7b88348cbc4bb99ccce807e24e61938cc480d9..c18b1b43c88377cd186e0938b7d52dc4ae0ecebf 100644
--- a/src/main/java/lab/App.java
+++ b/src/main/java/lab/App.java
@@ -4,7 +4,6 @@ import javafx.application.Application;
 import javafx.scene.Group;
 import javafx.scene.Scene;
 import javafx.scene.canvas.Canvas;
-import javafx.scene.canvas.GraphicsContext;
 import javafx.stage.Stage;
 import javafx.stage.WindowEvent;
 
@@ -31,7 +30,7 @@ public class App extends Application {
 			scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
 			primaryStage.setScene(scene);
 			primaryStage.resizableProperty().set(false);
-			primaryStage.setTitle("Java 1 - 2nd laboratory");
+			primaryStage.setTitle("Java 1 - 3rd laboratory");
 			primaryStage.show();
 			
 			//Exit program when main window is closed
@@ -50,13 +49,7 @@ public class App extends Application {
 	 *@return      nothing
 	 */
 	private void drawScene() {
-		//graphic context is used for a painting
-		GraphicsContext gc = canvas.getGraphicsContext2D();
-		int timeout = 200;
-		while (Routines.isEndOfThreadRequestedByJavaVM()) {
-			Routines.sleep(timeout);
-			double deltaT = timeout / 1000.;
-		}
+		new Laboratory().draw(canvas);
 	}
 	
 	private void exitProgram(WindowEvent evt) {
diff --git a/src/main/java/lab/BulletAnimated.java b/src/main/java/lab/BulletAnimated.java
new file mode 100644
index 0000000000000000000000000000000000000000..58acd4f302b0ecb7801c33da10cb096f082da020
--- /dev/null
+++ b/src/main/java/lab/BulletAnimated.java
@@ -0,0 +1,85 @@
+package lab;
+
+import javafx.geometry.Point2D;
+import javafx.scene.canvas.GraphicsContext;
+import javafx.scene.image.Image;
+
+public class BulletAnimated {
+
+	private Point2D position;
+	private Point2D start;
+	private Point2D speed;
+	private Point2D initialSpeed;
+	private double size;
+	private double mass = 2;
+	private double strenghtOfCannon = 2;
+	private double cannonLength = 100;
+	private boolean accelerate = true;
+	private boolean hitToGround = false;
+
+	private double crossSectionalArea;
+	private double dragCoefficient = 0.47;
+	
+	private Image image;
+	private World world;
+	private Cannon cannon;
+
+	public BulletAnimated(World world, Cannon cannon) {
+		this(world, cannon, new Point2D(0, 0), new Point2D(0, 0), 10);
+	}
+
+	public BulletAnimated(World world, Cannon cannon, Point2D start, Point2D speed, double size) {
+		this.start = start;
+		this.position = this.start;
+		this.initialSpeed = speed;
+		this.speed = speed;
+		this.size = size;
+		this.world = world;
+		this.cannon = cannon;
+		image = new Image(getClass().getResourceAsStream("fireball-transparent.gif"), size, size,
+				true, true);
+	}
+
+	public void draw(GraphicsContext gc) {
+		gc.save();
+		Point2D canvasPosition = world.getCanvasPoint(position);
+		gc.drawImage(image, canvasPosition.getX(), canvasPosition.getY());
+		gc.restore();
+	}
+
+	public void simulate(double timeStep) {
+		if (accelerate && start.distance(position) < cannonLength) {
+			double cannonAngle = cannon.getAngle(); 
+			speed = speed
+					.add(new Point2D(Math.cos(cannonAngle) * strenghtOfCannon, Math.sin(cannonAngle) * strenghtOfCannon)
+							.multiply(1 / mass));
+		} else if (!hitToGround) {
+			accelerate = false;
+			Point2D airResistanceforce = new Point2D(
+					-1. / 2 * crossSectionalArea * Constants.AIR_DENSITY * dragCoefficient * Math.pow(speed.getX(), 2),
+					-1. / 2 * crossSectionalArea * Constants.AIR_DENSITY * 0.47 * Math.pow(speed.getY(), 2));
+			Point2D acceleration = new Point2D(-airResistanceforce.getX() * mass,
+					(-Constants.GRAVITATIONAL_ACCELERATION + airResistanceforce.getY()) * mass);
+			speed = speed.add(acceleration.multiply(timeStep / 1000));
+		}
+		if (!hitToGround) {
+			position = position.add(speed);
+			if (!accelerate && position.getY() <= size / 2) {
+				hitToGround = true;
+				position = new Point2D(position.getX(), size / 2);
+			}
+		} else {
+			reload();
+		}
+		
+		
+	}
+
+	public void reload() {
+		position = start;
+		speed = initialSpeed;
+		hitToGround = false;
+		accelerate = true;
+	}
+	
+}
diff --git a/src/main/java/lab/Cannon.java b/src/main/java/lab/Cannon.java
new file mode 100644
index 0000000000000000000000000000000000000000..92c54310524127224b89db59c3b840b7709df328
--- /dev/null
+++ b/src/main/java/lab/Cannon.java
@@ -0,0 +1,49 @@
+package lab;
+
+import javafx.geometry.Point2D;
+import javafx.scene.canvas.GraphicsContext;
+import javafx.scene.paint.Color;
+import javafx.scene.transform.Affine;
+
+public class Cannon {
+
+	private int direction=-1;
+	private double angle = 0;
+	private Point2D position;
+	private Point2D size;
+	private World world;
+	
+	
+	 
+
+	public Cannon(World world, Point2D position, Point2D size) {
+		super();
+		this.world = world;
+		this.position = position;
+		this.size = size;
+	}
+
+	public void simulate(double timeStep) {
+		angle = angle + direction*0.8;
+		if(angle <=-90 || angle >= 0) {
+			direction*=-1;
+		}
+	}
+	
+	public void draw(GraphicsContext gc) {
+		gc.save();
+		Point2D worldPosition = world.getCanvasPoint(position);
+		gc.setFill(Color.BROWN);
+		gc.fillRect(worldPosition.getX()-10, worldPosition.getY()+size.getY(), size.getX()+20, size.getY()/2);
+		gc.fillOval(worldPosition.getX()-20, worldPosition.getY()+size.getY()/2, 40, 40);
+		gc.fillOval(worldPosition.getX()+size.getX(), worldPosition.getY()+size.getY()/2, 40, 40);
+		gc.setTransform(new Affine(Affine.rotate(angle, worldPosition.getX(), worldPosition.getY()+size.getY()/2)));
+		gc.setFill(Color.BLACK);
+		gc.fillRect(worldPosition.getX(), worldPosition.getY(), size.getX(), size.getY());
+		gc.restore();
+	}
+
+	public double getAngle() {
+		return (angle * -1) / 180 * Math.PI;
+	}
+}
diff --git a/src/main/java/lab/Constants.java b/src/main/java/lab/Constants.java
new file mode 100644
index 0000000000000000000000000000000000000000..92fb710258e1390b1856d44c935ec7b428dafd3a
--- /dev/null
+++ b/src/main/java/lab/Constants.java
@@ -0,0 +1,19 @@
+package lab;
+
+import javafx.scene.image.Image;
+
+public final class Constants {
+	private Constants() {}
+	
+	public static final double GRAVITATIONAL_ACCELERATION = 9.81;
+	
+	public static final double AIR_DENSITY = 1.2;
+	
+	static final int REFRESH_TIMEOUT = 25;
+	
+	public static final Image DRAGON_IMAGE;
+	
+	static{ 
+		DRAGON_IMAGE = new Image(Constants.class.getResourceAsStream("dragon.gif"));
+	}
+}
diff --git a/src/main/java/lab/Laboratory.java b/src/main/java/lab/Laboratory.java
new file mode 100644
index 0000000000000000000000000000000000000000..153a849dcee9aa4239c245f20a1115d8d0482ed6
--- /dev/null
+++ b/src/main/java/lab/Laboratory.java
@@ -0,0 +1,18 @@
+package lab;
+
+import javafx.scene.canvas.Canvas;
+import javafx.scene.canvas.GraphicsContext;
+
+public class Laboratory {
+
+	public void draw(Canvas canvas) {
+		GraphicsContext gc = canvas.getGraphicsContext2D();
+		World world = new World(canvas.getWidth(), canvas.getHeight());
+		while (!Routines.isEndOfThreadRequestedByJavaVM()) {
+			gc.clearRect(0, 0, canvas.getWidth(), canvas.getHeight());
+			world.draw(canvas);
+			Routines.sleep(25);
+			world.simulate(25);
+		}
+	}
+}
diff --git a/src/main/java/lab/Routines.java b/src/main/java/lab/Routines.java
index 89fcce7f1e20229e95fbdc50c69e2b0bb695f060..cfc1fc8b98187dce87ac262c34d087bbbc3d81a9 100644
--- a/src/main/java/lab/Routines.java
+++ b/src/main/java/lab/Routines.java
@@ -1,8 +1,8 @@
 package lab;
 
-public class Routines {
+public final class Routines {
 
-	
+	private Routines() {	}
 	
 	public static void sleep(int timeInMilisenonds) {
 		try {
diff --git a/src/main/java/lab/World.java b/src/main/java/lab/World.java
new file mode 100644
index 0000000000000000000000000000000000000000..a081a8e29466e4386790f35d429efddebd134fba
--- /dev/null
+++ b/src/main/java/lab/World.java
@@ -0,0 +1,53 @@
+package lab;
+
+import javafx.geometry.Point2D;
+import javafx.scene.canvas.Canvas;
+import javafx.scene.canvas.GraphicsContext;
+
+public class World {
+	private double width;
+	private double height;
+	private BulletAnimated bulletAnimatted;
+	private Cannon cannon;
+
+	public World(double width, double height) {
+		super();
+		this.width = width;
+		this.height = height;
+		cannon = new Cannon(this, new Point2D(50, 50), new Point2D(100, 20));
+		bulletAnimatted = new BulletAnimated(this, cannon, new Point2D(30, 60), new Point2D(0, 0), 40);
+	}
+
+	public Point2D getCanvasPoint(Point2D worldPoint) {
+		return new Point2D(worldPoint.getX(), height - worldPoint.getY());
+	}
+
+	public void draw(Canvas canvas) {
+		GraphicsContext gc = canvas.getGraphicsContext2D();
+		gc.clearRect(0, 0, canvas.getWidth(), canvas.getHeight());
+		cannon.draw(gc);
+		bulletAnimatted.draw(gc);
+	}
+
+	public void simulate(int timeDelta) {
+		bulletAnimatted.simulate(timeDelta);
+		cannon.simulate(timeDelta);
+	}
+
+	public double getWidth() {
+		return width;
+	}
+
+	public void setWidth(double width) {
+		this.width = width;
+	}
+
+	public double getHeight() {
+		return height;
+	}
+
+	public void setHeight(double height) {
+		this.height = height;
+	}
+
+}
diff --git a/src/main/resources/lab/dragon.gif b/src/main/resources/lab/dragon.gif
new file mode 100644
index 0000000000000000000000000000000000000000..aa36183f67ee4fbc6fa99b80076bdc7a7489a445
Binary files /dev/null and b/src/main/resources/lab/dragon.gif differ