diff --git a/pom.xml b/pom.xml
index 9ddb6d877ff6b6062f47a8c70248e7771ed92bc9..660a82b05bedb4618daa09a9c4760d186da8f4ec 100644
--- a/pom.xml
+++ b/pom.xml
@@ -22,26 +22,37 @@
 			<artifactId>javafx-fxml</artifactId>
 			<version>23</version>
 		</dependency>
-		<!-- https://mvnrepository.com/artifact/org.junit.jupiter/junit-jupiter-api -->
+		<!--
+		https://mvnrepository.com/artifact/org.junit.jupiter/junit-jupiter-api -->
 		<dependency>
 			<groupId>org.junit.jupiter</groupId>
 			<artifactId>junit-jupiter-api</artifactId>
 			<version>5.11.0</version>
 			<scope>test</scope>
 		</dependency>
-		<!-- https://mvnrepository.com/artifact/org.junit.jupiter/junit-jupiter-engine -->
+		<!--
+		https://mvnrepository.com/artifact/org.junit.jupiter/junit-jupiter-engine -->
 		<dependency>
 			<groupId>org.junit.jupiter</groupId>
 			<artifactId>junit-jupiter-engine</artifactId>
 			<version>5.11.0</version>
 			<scope>test</scope>
 		</dependency>
-		<!-- https://mvnrepository.com/artifact/org.junit.jupiter/junit-jupiter-params -->
+		<!--
+		https://mvnrepository.com/artifact/org.junit.jupiter/junit-jupiter-params -->
 		<dependency>
 			<groupId>org.junit.jupiter</groupId>
 			<artifactId>junit-jupiter-params</artifactId>
 			<version>5.11.0</version>
 			<scope>test</scope>
 		</dependency>
+		<!-- https://mvnrepository.com/artifact/org.reflections/reflections -->
+		<dependency>
+			<groupId>org.reflections</groupId>
+			<artifactId>reflections</artifactId>
+			<version>0.10.2</version>
+			<scope>test</scope>
+		</dependency>
+
 	</dependencies>
 </project>
diff --git a/src/main/java/lab/App.java b/src/main/java/lab/App.java
index 3a9e838f99d4c9e57926a33c1d4ffe23ef873f42..735a8b46f274cea82e92e2aa1445583b12e8eb00 100644
--- a/src/main/java/lab/App.java
+++ b/src/main/java/lab/App.java
@@ -1,54 +1,50 @@
 package lab;
 
-import javafx.animation.AnimationTimer;
 import javafx.application.Application;
-import javafx.scene.Group;
+import javafx.fxml.FXMLLoader;
+import javafx.scene.Parent;
 import javafx.scene.Scene;
-import javafx.scene.canvas.Canvas;
 import javafx.stage.Stage;
 import javafx.stage.WindowEvent;
 
 /**
- *  Class <b>App</b> - extends class Application and it is an entry point of the program
- * @author     Java I
+ * Class <b>App</b> - extends class Application and it is an entry point of the
+ * program
+ * 
+ * @author Java I
  */
 public class App extends Application {
 
+	private GameController gameController;
 	public static void main(String[] args) {
 		launch(args);
 	}
-	
-	private Canvas canvas;
-	private AnimationTimer timer;
-	
+
 	@Override
 	public void start(Stage primaryStage) {
 		try {
-			//Construct a main window with a canvas.  
-			Group root = new Group();
-			canvas = new Canvas(800, 400);
-			root.getChildren().add(canvas);
-			Scene scene = new Scene(root, 800, 400);
-			scene.getStylesheets().add(getClass().getResource("application.css").toExternalForm());
+			// Construct a main window with a canvas.
+			FXMLLoader gameLoader = new FXMLLoader(getClass().getResource("/lab/gameWindow.fxml"));
+			Parent root = gameLoader.load();
+			GameController gameController = gameLoader.getController();
+			Scene scene = new Scene(root);
 			primaryStage.setScene(scene);
 			primaryStage.resizableProperty().set(false);
 			primaryStage.setTitle("Java 1 - 1th laboratory");
 			primaryStage.show();
-			
-			//Exit program when main window is closed
+			// Exit program when main window is closed
 			primaryStage.setOnCloseRequest(this::exitProgram);
-			timer = new DrawingThread(canvas);
-			timer.start();
 		} catch (Exception e) {
 			e.printStackTrace();
 		}
 	}
+
 	@Override
 	public void stop() throws Exception {
-		timer.stop();
+		gameController.stop();
 		super.stop();
 	}
-	
+
 	private void exitProgram(WindowEvent evt) {
 		System.exit(0);
 	}
diff --git a/src/main/java/lab/DrawingThread.java b/src/main/java/lab/DrawingThread.java
index f5e0529bec78c3a83ae93743b43a80eff542258a..a0a63edcc76a220b086fd5dfbe55a8e493a910e3 100644
--- a/src/main/java/lab/DrawingThread.java
+++ b/src/main/java/lab/DrawingThread.java
@@ -28,4 +28,8 @@ public class DrawingThread extends AnimationTimer {
 		lastTime = now;
 	}
 
+	public Level getLevel() {
+		return level;
+	}
+
 }
diff --git a/src/main/java/lab/GameController.java b/src/main/java/lab/GameController.java
new file mode 100644
index 0000000000000000000000000000000000000000..6d2f4a68c0fd38dd48256bd97d10080b9accf44b
--- /dev/null
+++ b/src/main/java/lab/GameController.java
@@ -0,0 +1,57 @@
+package lab;
+
+import javafx.beans.value.ChangeListener;
+import javafx.beans.value.ObservableValue;
+import javafx.event.ActionEvent;
+import javafx.fxml.FXML;
+import javafx.geometry.Point2D;
+import javafx.scene.canvas.Canvas;
+import javafx.scene.control.Slider;
+
+public class GameController {
+
+	@FXML
+	private Slider angle;
+
+	private DrawingThread timer;
+
+    @FXML
+    private Canvas canvas;
+
+    @FXML
+    private Slider speed;
+
+    @FXML
+    void spawn(ActionEvent event) {
+		timer.getLevel().getPlayer().spawn();
+
+    }
+
+    @FXML
+    void initialize() {
+        assert angle != null : "fx:id=\"angle\" was not injected: check your FXML file 'gameWindow.fxml'.";
+        assert canvas != null : "fx:id=\"canvas\" was not injected: check your FXML file 'gameWindow.fxml'.";
+        assert speed != null : "fx:id=\"speed\" was not injected: check your FXML file 'gameWindow.fxml'.";
+		timer = new DrawingThread(canvas);
+		timer.start();
+		angle.valueProperty().addListener(new ChangeListener<Number>() {
+			@Override
+			public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) {
+				timer.getLevel().getPlayer().setAngle(newValue.doubleValue());
+			}
+		});
+		speed.valueProperty().addListener(new ChangeListener<Number>() {
+			@Override
+			public void changed(ObservableValue<? extends Number> observable, Number oldValue, Number newValue) {
+				timer.getLevel().getPlayer().setSpeed(newValue.doubleValue());
+			}
+		});
+
+    }
+
+	public void stop() {
+		timer.stop();
+	}
+
+    
+}
diff --git a/src/main/java/lab/Level.java b/src/main/java/lab/Level.java
index d6f9031fc674d48d37017570351da1a0f5501291..e5497d23432219adf92c622989a93812108d5f32 100644
--- a/src/main/java/lab/Level.java
+++ b/src/main/java/lab/Level.java
@@ -10,15 +10,17 @@ public class Level {
 	private double width;
 	private double height;
 	private DrawableSimulable[] entities;
-
+	private Player player;
+	
 	public Level(double width, double height) {
 		this.width = width;
 		this.height = height;
+		player = new Player(this, new Point2D(20, 250), new Point2D(100, -20));
 		entities = new DrawableSimulable[9];
 		entities[0] = new NicerObstacle(this, new Point2D(20, 150));
 		entities[1] = new Obstacle(this, new Point2D(300, 200), new Dimension2D(80, 40));
 		entities[2] = new Obstacle(this);
-		entities[3] =  new Player(this, new Point2D(20, 250), new Point2D(100, -20));
+		entities[3] = player;
 		for (int i = 4; i < entities.length; i++) {
 			entities[i] = new Monster(this);
 		}
@@ -57,4 +59,9 @@ public class Level {
 	public double getHeight() {
 		return height;
 	}
+
+	public Player getPlayer() {
+		return player;
+	}
+	
 }
diff --git a/src/main/java/lab/Player.java b/src/main/java/lab/Player.java
index 7450176246da92dce064b66301358f373d964842..dd51953dc6ba03b42203e879e5b8a6ac96196435 100644
--- a/src/main/java/lab/Player.java
+++ b/src/main/java/lab/Player.java
@@ -12,6 +12,8 @@ import javafx.scene.transform.Rotate;
 public class Player extends WorldEntity implements Collisionable {
 	private static final Random RANDOM = new Random();
 	private Point2D speed;
+	private double speedSize;
+	private double angle;
 
 	public Player(Level level, Point2D position, Point2D speed) {
 		super(level, position);
@@ -58,4 +60,26 @@ public class Player extends WorldEntity implements Collisionable {
 
 	}
 
+	public void setSpeed(double speedSize) {
+		this.speedSize = speedSize;
+		updateSpeed();
+
+	}
+
+	private void updateSpeed() {
+		speed = new Point2D(Math.cos(angle) * speedSize, Math.sin(angle) * speedSize);
+	}
+
+	public void setAngle(double angle) {
+		this.angle = Math.toRadians(angle);
+		updateSpeed();
+	}
+	
+	public void spawn() {
+		position = new Point2D(
+				RANDOM.nextDouble(0, level.getWidth()*0.5), 
+				RANDOM.nextDouble(level.getHeight()*0.5, level.getHeight()*0.9)); 
+
+	}
+
 }
diff --git a/src/main/resources/lab/gameWindow.fxml b/src/main/resources/lab/gameWindow.fxml
new file mode 100644
index 0000000000000000000000000000000000000000..55d798a6be852770de8550ce0b785dd141b9fe39
--- /dev/null
+++ b/src/main/resources/lab/gameWindow.fxml
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<?import javafx.geometry.Insets?>
+<?import javafx.scene.Cursor?>
+<?import javafx.scene.canvas.Canvas?>
+<?import javafx.scene.control.Button?>
+<?import javafx.scene.control.Slider?>
+<?import javafx.scene.layout.BorderPane?>
+<?import javafx.scene.layout.HBox?>
+<?import javafx.scene.text.Font?>
+
+<BorderPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="459.0" prefWidth="824.0" xmlns="http://javafx.com/javafx/21" xmlns:fx="http://javafx.com/fxml/1" fx:controller="lab.GameController">
+   <bottom>
+      <HBox alignment="TOP_CENTER" prefHeight="66.0" prefWidth="824.0" BorderPane.alignment="CENTER">
+         <children>
+            <Slider fx:id="speed" majorTickUnit="15.0" max="200.0" maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" min="50.0" minorTickCount="5" prefHeight="68.0" prefWidth="736.0" showTickLabels="true" showTickMarks="true" HBox.hgrow="ALWAYS" />
+            <Button maxWidth="1.7976931348623157E308" mnemonicParsing="false" onAction="#spawn" prefHeight="46.0" prefWidth="147.0" style="-fx-background-color: blue;" text="Spawn" textAlignment="CENTER" textFill="WHITE" textOverrun="CLIP">
+               <font>
+                  <Font name="System Bold" size="22.0" />
+               </font>
+               <cursor>
+                  <Cursor fx:constant="CROSSHAIR" />
+               </cursor>
+               <opaqueInsets>
+                  <Insets bottom="10.0" left="10.0" right="10.0" top="10.0" />
+               </opaqueInsets>
+               <HBox.margin>
+                  <Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />
+               </HBox.margin>
+            </Button>
+         </children>
+      </HBox>
+   </bottom>
+   <center>
+      <Canvas fx:id="canvas" height="385.0" width="749.0" BorderPane.alignment="CENTER">
+         <BorderPane.margin>
+            <Insets />
+         </BorderPane.margin>
+      </Canvas>
+   </center>
+   <left>
+      <Slider fx:id="angle" blockIncrement="25.0" majorTickUnit="30.0" max="360.0" maxHeight="1.7976931348623157E308" maxWidth="1.7976931348623157E308" minorTickCount="5" orientation="VERTICAL" showTickLabels="true" showTickMarks="true" BorderPane.alignment="CENTER" />
+   </left>
+</BorderPane>
diff --git a/src/test/java/jez04/structure/test/ClassStructureTest.java b/src/test/java/jez04/structure/test/ClassStructureTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..aaf98cc9a4d18a9790d65d4b89f57861e52ad67b
--- /dev/null
+++ b/src/test/java/jez04/structure/test/ClassStructureTest.java
@@ -0,0 +1,220 @@
+package jez04.structure.test;
+
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.io.ByteArrayOutputStream;
+import java.io.PrintStream;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+import org.reflections.Configuration;
+import org.reflections.Reflections;
+import org.reflections.scanners.Scanners;
+import org.reflections.util.ConfigurationBuilder;
+
+import javafx.event.ActionEvent;
+import javafx.fxml.FXML;
+import javafx.geometry.Rectangle2D;
+import javafx.scene.canvas.GraphicsContext;
+
+class ClassStructureTest {
+
+	private static final String drawableSimulableName = "DrawableSimulable";
+	private static final String collisionableName = "Collisionable";
+
+	Set<String> allClasses = getNameOfAllClasses();
+
+	@Test
+	void gameControllerExistenceTest() {
+		classExist("GameController");
+		Class<?> c = getClass("GameController");
+		hasPropertyWithAnnotation(c, ".*", FXML.class);
+		hasMethodRegexp(c, ".*", void.class, ActionEvent.class);
+	}
+	@Test
+	void gameControllerFxmlTest() {
+		classExist("GameController");
+		Class<?> c = getClass("GameController");
+		hasPropertyWithAnnotation(c, ".*", FXML.class);
+	}
+	@Test
+	void gameControllerActionEventTest() {
+		classExist("GameController");
+		Class<?> c = getClass("GameController");
+		hasMethodRegexp(c, ".*", void.class, ActionEvent.class);
+	}
+
+	@Test
+	void cannonExistenceTest() {
+		classExist("Player");
+	}
+	@Test
+	void cannonExistenceSetAngleTest() {
+		classExist("Player");
+		Class<?> c = getClass("Player");
+		hasMethod(c, "setAngle");
+	}
+	@Test
+	void cannonExistenceSetSpeedTest() {
+		classExist("Player");
+		Class<?> c = getClass("Player");
+		hasMethod(c, "setSpeed");
+	}
+
+
+	private void isInterface(Class<?> c) {
+		assertTrue(c.isInterface(), c.getName() + " have to be interface.");
+	}
+
+	private void classExist(String name) {
+		assertTrue(allClasses.stream().anyMatch(c -> c.endsWith(name)), "Interface " + name + " not found");
+	}
+
+	private Class<?> getClass(String name) {
+		String className = allClasses.stream().filter(c -> c.endsWith(name)).findAny().orElse(null);
+		if (className == null) {
+			Assertions.fail("Class " + name + " not found.");
+		}
+		try {
+			return Class.forName(className);
+		} catch (ClassNotFoundException e) {
+			final ByteArrayOutputStream baos = new ByteArrayOutputStream();
+			try (PrintStream ps = new PrintStream(baos, true)) {
+				e.printStackTrace(ps);
+			} catch (Exception e2) {
+				Assertions.fail(e2.getMessage());
+			}
+			String stackTrace = baos.toString();
+			Assertions.fail("Class " + name + " not found.\n" + stackTrace);
+			return null;
+		}
+	}
+
+	private void hasProperty(Class<?> classDef, String propertyNameRegexp, Class<?> type, boolean array) {
+		List<Field> fields = Arrays.asList(classDef.getDeclaredFields());
+		assertTrue(fields.stream().anyMatch(f -> {
+			if (f.getName().matches(propertyNameRegexp)) {
+				if (array) {
+					return f.getType().isArray() && f.getType().getComponentType().equals(type);
+				} else {
+					return f.getType().equals(type);
+				}
+			}
+			return false;
+		}), "No field " + propertyNameRegexp + " of type " + type.getName() + " (is array " + array + ") in class "
+				+ classDef.getName());
+	}
+
+	private void hasPropertyWithAnnotation(Class<?> classDef, String propertyNameRegexp, Class<?> annotation) {
+		List<Field> fields = Arrays.asList(classDef.getDeclaredFields());
+		assertTrue(
+				fields.stream().filter(f -> f.getName().matches(propertyNameRegexp))
+						.flatMap(f -> Arrays.asList(
+								f.getAnnotations()).stream()).map(a -> a.annotationType()).anyMatch(a -> 
+								a.equals(annotation)),
+				"No field " + propertyNameRegexp + " with annotation " + annotation.getName() + " in class "
+						+ classDef.getName());
+	}
+
+	private void hasMethod(Class<?> interfaceDef, String methodName, Class<?> returnType) {
+		List<Method> methods = Arrays.asList(interfaceDef.getDeclaredMethods());
+		assertTrue(methods.stream().anyMatch(m -> m.getName().contains(methodName)), "No method " + methodName);
+		assertTrue(
+				methods.stream().filter(m -> m.getName().contains(methodName))
+						.anyMatch(m -> m.getReturnType().equals(returnType)),
+				"Method " + methodName + " not return " + returnType.getName());
+	}
+
+	private void hasMethod(Class<?> interfaceDef, String methodName, Class<?> returnType, Class<?>... params) {
+		List<Method> methods = Arrays.asList(interfaceDef.getDeclaredMethods());
+		assertTrue(methods.stream().anyMatch(m -> m.getName().contains(methodName)), "No method " + methodName);
+		assertTrue(
+				methods.stream().filter(m -> m.getName().contains(methodName))
+						.filter(m -> m.getReturnType().equals(returnType))
+						.anyMatch(m -> Arrays.asList(m.getParameterTypes()).containsAll(Arrays.asList(params))),
+				"Method " + methodName + " has no all parrams:"
+						+ Arrays.asList(params).stream().map(Class::getName).collect(Collectors.joining(", ")));
+	}
+
+	private void hasMethodRegexp(Class<?> interfaceDef, String methodNameRegexp, Class<?> returnType, Class<?>... params) {
+		List<Method> methods = Arrays.asList(interfaceDef.getDeclaredMethods());
+		assertTrue(methods.stream().anyMatch(m -> m.getName().matches(methodNameRegexp)), "No method " + methodNameRegexp);
+		assertTrue(
+				methods.stream().filter(m -> m.getName().matches(methodNameRegexp))
+						.filter(m -> m.getReturnType().equals(returnType))
+						.anyMatch(m -> 
+						Arrays.asList(m.getParameterTypes()).containsAll(Arrays.asList(params))),
+				"Method " + methodNameRegexp + " has no all parrams:"
+						+ Arrays.asList(params).stream().map(Class::getName).collect(Collectors.joining(", ")));
+	}
+	private void hasMethod(Class<?> interfaceDef, boolean finalTag, boolean abstractTag, String methodName,
+			Class<?> returnType, Class<?>... params) {
+		List<Method> methods = Arrays.asList(interfaceDef.getDeclaredMethods());
+		assertTrue(methods.stream().anyMatch(m -> m.getName().contains(methodName)), "No method " + methodName);
+		assertTrue(
+				methods.stream().filter(m -> m.getName().contains(methodName))
+						.filter(m -> m.getReturnType().equals(returnType)
+								&& (Modifier.isAbstract(m.getModifiers()) == abstractTag)
+								&& (Modifier.isFinal(m.getModifiers()) == finalTag))
+						.anyMatch(m -> Arrays.asList(m.getParameterTypes()).containsAll(Arrays.asList(params))),
+				"Method " + methodName + " has no all params:"
+						+ Arrays.asList(params).stream().map(Class::getName).collect(Collectors.joining(", ")));
+	}
+
+	private void hasImplements(Class<?> clazz, String... interfaceNames) {
+		List<Class<?>> interfaces = new ArrayList<>();
+		Arrays.asList(interfaceNames).stream().map(name -> getClass(name)).forEach(c -> interfaces.add(c));
+		assertTrue(Arrays.asList(clazz.getInterfaces()).containsAll(interfaces), "Class not implements all interfaces:"
+				+ interfaces.stream().map(Class::getName).collect(Collectors.joining(", ")));
+	}
+
+	private void hasExtends(Class<?> clazz, String parentName) {
+		Class<?> parent = getClass(parentName);
+		assertTrue(clazz.getSuperclass().equals(parent),
+				"Class " + clazz.getName() + " not extends class " + parentName);
+	}
+
+	private void hasMethod(Class<?> interfaceDef, String methodName) {
+		List<Method> methods = Arrays.asList(interfaceDef.getMethods());
+		assertTrue(methods.stream().anyMatch(m -> m.getName().contains(methodName)), "No method " + methodName);
+	}
+
+	private Set<String> getNameOfAllClasses() {
+		List<String> initClassesName = Arrays.asList("lab.Routines", "lab.App", "lab.DrawingThread");
+		for (String className : initClassesName) {
+			try {
+				Class.forName(className);
+			} catch (ClassNotFoundException e) {
+				System.out.println(String.format("Class '%s' cannot be loaded: %s", className, e.getMessage()));
+			}
+		}
+		Set<String> allClasses = new HashSet<>();
+		for (Package p : Package.getPackages()) {
+			if (p.getName().startsWith("java.") || p.getName().startsWith("com.") || p.getName().startsWith("jdk.")
+					|| p.getName().startsWith("javafx.") || p.getName().startsWith("org.")
+					|| p.getName().startsWith("sun.") || p.getName().startsWith("javax.")
+					|| p.getName().startsWith("javassist")) {
+				continue;
+			}
+			System.out.println(p.getName());
+			Configuration conf = new ConfigurationBuilder().addScanners(Scanners.SubTypes.filterResultsBy(pc -> true))
+					.forPackages(p.getName());
+			Reflections reflections = new Reflections(conf);
+			allClasses.addAll(reflections.getAll(Scanners.SubTypes.filterResultsBy(c -> {
+				System.out.println(c);
+				return true;
+			})));
+		}
+		return allClasses;
+	}
+}