diff --git a/src/00-welcome-and-example/Example.ts b/src/00-welcome-and-example/Example.ts
index 858c7edc17b79188832fdcc27ccc646d76eb4331..a5a6f7d4c0488d025800ebdec65f738beab1cd43 100644
--- a/src/00-welcome-and-example/Example.ts
+++ b/src/00-welcome-and-example/Example.ts
@@ -19,7 +19,7 @@ class Example extends PipelineData {
         const point2d = new Point2d(.1, 32, new THREE.Color(0xffffff));
         const point3d = new Point3d(.1, 32, new THREE.Color(0xffffff));
         const line1d = new Line1d(pointsA, new THREE.Color(0xffffff));
-        const line2d = new Line2d(pointsB, .001, new THREE.Color(0xffffff));
+        const line2d = new Line2d(pointsB, .01, new THREE.Color(0xffffff));
         const mesh = new Mesh("../assets/suzanne.obj", new THREE.Color(0xffffff));
         
         this.addShape(point2d, true, true);
diff --git a/src/01-bezierCurves/CubicBezierCurve.ts b/src/01-bezierCurves/CubicBezierCurve.ts
new file mode 100644
index 0000000000000000000000000000000000000000..5b40aa69fea3f38b4598ac49476b7abe050eca6b
--- /dev/null
+++ b/src/01-bezierCurves/CubicBezierCurve.ts
@@ -0,0 +1,85 @@
+import * as THREE from "three";
+import { Vector3 } from "three";
+
+abstract class CubicBezierAlgorithm {
+    /**
+     * @param controlPointsPosition an array with vectors that give the positons of the control points. The 
+     * first vector is the start point, the second vector is the first control point, the third vector is the
+     * second control point and the fourth vector is the end point.
+     * @param t the time at which the curve should be evaluated (0.0 - 1.0)
+     */
+    abstract evaluatePosition(controlPointsPosition: Array<THREE.Vector3>, t: number): THREE.Vector3;
+}
+
+class BernsteinAlgorithm extends CubicBezierAlgorithm {
+    evaluatePosition(positions:  Array<THREE.Vector3>, t: number): THREE.Vector3 {
+        const c = BernsteinAlgorithm.calculateCoefficients(t);
+        const x = c[0] * positions[0].x + c[1] * positions[1].x + c[2] * positions[2].x + c[3] * positions[3].x;
+        const y = c[0] * positions[0].y + c[1] * positions[1].y + c[2] * positions[2].y + c[3] * positions[3].y;
+        const z = c[0] * positions[0].z + c[1] * positions[1].z + c[2] * positions[2].z + c[3] * positions[3].z;
+        return new THREE.Vector3(x, y, z);
+    }
+
+    /**
+     * Weight the contribution of controlpoints by calculating the values of the different terms of the
+     * Bernstein polynome. The terms yield a coefficient of the different points.
+     *      p(t) = (1-t)^3 * p0 + 3 * (1-t)^2 * t * p1 +
+     *             3 * (1-t) * t^2 * p2 + t^3 * p3
+     */
+    public static calculateCoefficients(t: number): number[]{
+        const u0 = 1.0 * Math.pow(1.0 - t, 3.0) * Math.pow(t, 0.0);
+        const u1 = 3.0 * Math.pow(1.0 - t, 2.0) * Math.pow(t, 1.0);
+        const u2 = 3.0 * Math.pow(1.0 - t, 1.0) * Math.pow(t, 2.0);
+        const u3 = 1.0 * Math.pow(1.0 - t, 0.0) * Math.pow(t, 3.0);
+        return [u0, u1, u2, u3];
+    }
+}
+
+class DeCasteljauAlgorithm extends CubicBezierAlgorithm {
+    evaluatePosition(positions: Array<THREE.Vector3>, t: number): THREE.Vector3 {
+        while (positions.length > 1)
+            positions = DeCasteljauAlgorithm.Iteration(positions, t);
+        return positions[0];
+    }
+
+    public static Iteration(points: Array<THREE.Vector3>, t: number): Array<THREE.Vector3>
+    {
+        var result = new Array<THREE.Vector3>();
+        for (let i = 0; i < points.length - 1; i++)
+            result.push(points[i + 1].clone().sub(points[i]).multiplyScalar(t).add(points[i]));
+        return result;
+    }
+}
+
+class CubicBezierCurve {
+
+    private _algorithm: CubicBezierAlgorithm;
+    private _controlPoints: Array<THREE.Vector3>;
+
+    constructor(controlPoints: Array<THREE.Vector3>, algorithm: CubicBezierAlgorithm) {
+        this._controlPoints = controlPoints;
+        this._algorithm = algorithm;
+    }
+
+    public evaluatePosition(t: number): THREE.Vector3 {
+        return this._algorithm.evaluatePosition(this._controlPoints, t);
+    }
+
+    public evaluatePositions(resolution: number): Array<THREE.Vector3> {
+        const positions = new Array<THREE.Vector3>();
+        for (let i = 0; i < resolution + 1; i++)
+            positions.push(this.evaluatePosition(i / resolution));
+        return positions;
+    }
+
+    public get controlPoints(): Array<THREE.Vector3> {
+        return this._controlPoints;
+    }
+
+    public set controlPoints(value: Array<THREE.Vector3>) {
+        this._controlPoints = value;
+    }
+
+}
+
+export {CubicBezierAlgorithm, BernsteinAlgorithm, DeCasteljauAlgorithm, CubicBezierCurve};
\ No newline at end of file
diff --git a/src/01-bezierCurves/Curve.ts b/src/01-bezierCurves/Curve.ts
new file mode 100644
index 0000000000000000000000000000000000000000..d086d9926d954b9b34c5bf12345893b8270a2b4c
--- /dev/null
+++ b/src/01-bezierCurves/Curve.ts
@@ -0,0 +1,47 @@
+import { Line2d, Point2d } from "../core/Shapes";
+import { CubicBezierCurve, DeCasteljauAlgorithm } from "./CubicBezierCurve";
+
+interface CurveParameter {
+    pointA: Point2d;
+    tangentA: Point2d;
+    pointB: Point2d;
+    tangentB: Point2d;
+
+    offset: number;
+    resolution: number;
+    linewidth: number;
+    color: THREE.Color;
+}
+
+class Curve extends Line2d {
+
+    private _generator: CubicBezierCurve;
+    private _resolution: number;
+    private _offset: number;
+
+    constructor(parameter: CurveParameter) {
+        super([], parameter.linewidth, parameter.color);
+
+        this._resolution = parameter.resolution;
+        this._offset = parameter.offset;
+
+        const positions = new Array<THREE.Vector3>();
+        positions.push(parameter.pointA.position);
+        positions.push(parameter.tangentA.position);
+        positions.push(parameter.tangentB.position);
+        positions.push(parameter.pointB.position);
+
+        this._generator = new CubicBezierCurve(positions, new DeCasteljauAlgorithm());
+    }
+
+    public update(_deltaTime: number): void {
+        this._points = this._generator.evaluatePositions(this._resolution);
+        this._points.forEach((point) => {
+            point.z = this._offset;
+        });
+        this._createLine();
+    }
+}
+
+export { Curve };
+export type { CurveParameter };
diff --git a/src/01-bezierCurves/DemoBezier.ts b/src/01-bezierCurves/DemoBezier.ts
index 7589d58160e880f51021430d0975e510aebb133e..01ff12c93c394c2068368f7917bfb13d9b8e13fd 100644
--- a/src/01-bezierCurves/DemoBezier.ts
+++ b/src/01-bezierCurves/DemoBezier.ts
@@ -1,13 +1,39 @@
 import * as THREE from 'three';
 import { PipelineData } from "../core/Pipeline";
 import { Point2d } from "../core/Shapes";
+import { Curve } from './Curve';
 
 class BezierDemo extends PipelineData {
     public constructor() { super(); }
 
     data(): void {
-        const point = new Point2d(.1, 36, new THREE.Color(0x00ff00));
-        this.addShape(point, true, true);
+
+        const pointA = new Point2d(.05, 36, new THREE.Color(0x00ff00));
+        const tangentA = new Point2d(.025, 36, new THREE.Color(0x00ff00));
+        const pointB = new Point2d(.05, 36, new THREE.Color(0xff0000));
+        const tangentB = new Point2d(.025, 36, new THREE.Color(0xff0000));
+
+        pointA.material = new THREE.MeshBasicMaterial({ color: 0x00ff00, wireframe: false });;
+        tangentA.material = new THREE.MeshBasicMaterial({ color: 0x00ff00, wireframe: false });;
+        pointB.material = new THREE.MeshBasicMaterial({ color: 0xff0000, wireframe: false });;
+        tangentB.material = new THREE.MeshBasicMaterial({ color: 0xff0000, wireframe: false });;
+
+        const curve = new Curve({
+            pointA: pointA,
+            tangentA: tangentA,
+            pointB: pointB,
+            tangentB: tangentB,
+            resolution: 128,
+            linewidth: .02,
+            offset: -1,
+            color: new THREE.Color(0xffffff)
+        });
+
+        this.addShape(pointA, true, true);
+        this.addShape(tangentA, true, true);
+        this.addShape(pointB, true, true);
+        this.addShape(tangentB, true, true);
+        this.addShape(curve, true, false);
     }
 }
 
diff --git a/src/Main.ts b/src/Main.ts
index 7d2cc2ce0d79b6c912d1de9d73637becc4915775..39ae54112b2f0fb94da3123acc4a912c885a76c1 100644
--- a/src/Main.ts
+++ b/src/Main.ts
@@ -7,21 +7,28 @@ import { QuaternionDemo } from "./02-quaternion/demoQuaternion";
 import { SimulationDemo } from "./03-simulation/demoSimulation";
 
 window.onload = () => {
-    const pipeline = new Pipeline();
     const ref = window.location.href
 
     let scenario: PipelineData;
+    let pipeline: Pipeline;
     if (ref.includes("bezier")) {
+        pipeline = new Pipeline(false);
+        pipeline.orbitControls.enableRotate = false;
         scenario = new BezierDemo();
     } else if (ref.includes("quaternion")) {
+        pipeline = new Pipeline();
         scenario = new QuaternionDemo();
     } else if (ref.includes("simulation")) {
+        pipeline = new Pipeline();
         scenario = new SimulationDemo();
     } else if (ref.includes("example")) {
+        pipeline = new Pipeline();
         scenario = new Example();
     } else {
+        pipeline = new Pipeline();
         scenario = new Demo();
     }
+
     pipeline.init(scenario);
     pipeline.loop();
 }
diff --git a/src/core/Pipeline.ts b/src/core/Pipeline.ts
index 60178015278e3257c10b8127e6fb559f1ea50eff..98ff748bc8897b883f617d36d5f45928e9c748c1 100644
--- a/src/core/Pipeline.ts
+++ b/src/core/Pipeline.ts
@@ -18,8 +18,6 @@ abstract class PipelineData {
     draggables: Array<THREE.Object3D>;
     scene: THREE.Scene;
 
-    allowOrbit: boolean = true;
-
     constructor() {
         this.observers = new Array<PipelineObserver>();
         this.draggables = new Array<THREE.Object3D>();
@@ -48,25 +46,39 @@ abstract class PipelineData {
 class Pipeline {
 
     public readonly renderer: THREE.WebGLRenderer;
-    public readonly camera: THREE.PerspectiveCamera;
+    public readonly camera: THREE.Camera;
 
-    public readonly orbitControls: OrbitControls;
-    public readonly dragControls: DragControls;
+    public orbitControls: OrbitControls;
+    public dragControls: DragControls;
 
     private scene?: THREE.Scene;
     private observer: Array<PipelineObserver>;
     private startTime: number;
 
-    constructor() {
+    constructor(perspective: boolean = true) {
         // CREATE CAMERA
-        this.camera = new THREE.PerspectiveCamera(75, 
-            window.innerWidth / window.innerHeight, 0.1, 1000);
-        this.camera.position.z = 5;
 
-        window.addEventListener('resize', () => {
-            this.camera.aspect = window.innerWidth / window.innerHeight
-            this.camera.updateProjectionMatrix()
-        }, false);
+        if (perspective) {
+            this.camera = new THREE.PerspectiveCamera(75, 
+                window.innerWidth / window.innerHeight, 0.1, 1000);
+            this.camera.position.z = 5;
+    
+            window.addEventListener('resize', () => {
+                (this.camera as THREE.PerspectiveCamera).aspect = window.innerWidth / window.innerHeight;
+                (this.camera as THREE.PerspectiveCamera).updateProjectionMatrix()
+            }, false);
+        } else {
+            const aspect = window.innerWidth / window.innerHeight;
+            this.camera = new THREE.OrthographicCamera(-aspect, aspect, 1, -1, 0.1, 1000);
+            this.camera.position.z = 1;
+
+            window.addEventListener('resize', () => {
+                const aspect = window.innerWidth / window.innerHeight;
+                (this.camera as THREE.OrthographicCamera).left = -aspect;
+                (this.camera as THREE.OrthographicCamera).right = aspect;
+                (this.camera as THREE.OrthographicCamera).updateProjectionMatrix()
+            }, false);
+        }
 
         // CREATE RENDERER
         this.renderer = new THREE.WebGLRenderer({
diff --git a/src/core/Shapes.ts b/src/core/Shapes.ts
index dc6485bc70b0bb3ac9924db281be430b9acc2968..8ef39e04ba9cb6dc7100ba33f86ba8adb2e45d33 100644
--- a/src/core/Shapes.ts
+++ b/src/core/Shapes.ts
@@ -25,34 +25,6 @@ abstract class Shape implements PipelineObserver {
     update(_deltaTime: number): void {/* INTENTIONAL */ }
 }
 
-class DebugHelper implements PipelineObserver {
-
-    private _debug: Map<Shape, THREE.BoxHelper>;
-
-    constructor() {
-        this._debug = new Map<Shape, THREE.BoxHelper>();
-    }
-
-    public get debug(): Array<THREE.BoxHelper> {
-        return Array.from(this._debug.values());
-    }
-
-    public add(shape: Shape): void {
-        const box = new THREE.BoxHelper(shape.object3d);
-        this._debug.set(shape, box);
-    }
-
-    public remove(shape: Shape): void {
-        this._debug.delete(shape);
-    }
-
-    update(_deltaTime: number): void {
-        for (let [_, box] of this._debug) {
-            box.update();
-        }
-    }
-}
-
 abstract class Point extends Shape {
     private _geometry: THREE.BufferGeometry;
     private _material: THREE.MeshBasicMaterial | THREE.MeshLambertMaterial;
@@ -93,38 +65,13 @@ abstract class Point extends Shape {
     }
 
     public set material(material: THREE.MeshBasicMaterial | THREE.MeshLambertMaterial) {
-        this._material = material;
+        this._material = material.clone();
         this._material.color = this._color;
         (this.object3d as THREE.Mesh).material = material;
     }
     
 }
 
-class Point2d extends Point {
-    constructor(radius: number, resolution: number, color: THREE.Color) {
-        super(radius, resolution, color, new THREE.CircleBufferGeometry(radius, resolution), 
-        new THREE.MeshBasicMaterial({ color: color.getHex(), wireframe: true }));
-    }
-
-    protected _createMesh(): void {
-        (this._object3d as THREE.Mesh).geometry.dispose();
-        (this._object3d as THREE.Mesh).geometry = new THREE.CircleBufferGeometry(this._radius, this._resolution);
-    }
-} 
-
-class Point3d extends Point {
-    constructor(radius: number, resolution: number, color: THREE.Color) {
-        super(radius, resolution, color, new THREE.SphereBufferGeometry(radius, resolution, resolution / 2), 
-        new THREE.MeshBasicMaterial({ color: color.getHex(), wireframe: true }));
-    }
-
-    protected _createMesh(): void {
-        (this._object3d as THREE.Mesh).geometry.dispose();
-        (this._object3d as THREE.Mesh).geometry = new THREE.SphereBufferGeometry(this._radius, this._resolution, 
-            this._resolution / 2);
-    }
-}
-
 abstract class Line extends Shape {
     protected _geometry: THREE.BufferGeometry;
     protected _material: LineMaterial | THREE.LineBasicMaterial;
@@ -142,7 +89,11 @@ abstract class Line extends Shape {
         this._geometry = geometry;
         this._material = material;
 
-        this._object3d = new THREE.Line(this._geometry, this._material);
+        if (this._material instanceof LineMaterial) {
+            this._object3d = new THREE.Mesh(this._geometry, this._material);
+        } else {
+            this._object3d = new THREE.Line(this._geometry, this._material);
+        }
     }
 
     protected abstract _createLine(): void;
@@ -158,13 +109,69 @@ abstract class Line extends Shape {
     }
 
     public set material(material: LineMaterial | THREE.LineBasicMaterial) {
-        this._material = material;
+        this._material = material.clone();
         this._material.color = this._color;
         (this._object3d as THREE.Line).material = material;
     }
 }
 
+class DebugHelper implements PipelineObserver {
+
+    private _debug: Map<Shape, THREE.BoxHelper>;
+
+    constructor() {
+        this._debug = new Map<Shape, THREE.BoxHelper>();
+    }
+
+    public get debug(): Array<THREE.BoxHelper> {
+        return Array.from(this._debug.values());
+    }
+
+    public add(shape: Shape): void {
+        const box = new THREE.BoxHelper(shape.object3d);
+        this._debug.set(shape, box);
+    }
+
+    public remove(shape: Shape): void {
+        this._debug.delete(shape);
+    }
+
+    update(_deltaTime: number): void {
+        for (let [_, box] of this._debug) {
+            box.update();
+        }
+    }
+}
+
+class Point2d extends Point {
+    constructor(radius: number, resolution: number, color: THREE.Color) {
+        super(radius, resolution, color, new THREE.CircleBufferGeometry(radius, resolution), 
+        new THREE.MeshBasicMaterial({ color: color.getHex(), wireframe: true }));
+    }
+
+    protected _createMesh(): void {
+        (this._object3d as THREE.Mesh).geometry.dispose();
+        (this._object3d as THREE.Mesh).geometry = new THREE.CircleBufferGeometry(this._radius, this._resolution);
+    }
+} 
+
+class Point3d extends Point {
+    constructor(radius: number, resolution: number, color: THREE.Color) {
+        super(radius, resolution, color, new THREE.SphereBufferGeometry(radius, resolution, resolution / 2), 
+        new THREE.MeshBasicMaterial({ color: color.getHex(), wireframe: true }));
+    }
+
+    protected _createMesh(): void {
+        (this._object3d as THREE.Mesh).geometry.dispose();
+        (this._object3d as THREE.Mesh).geometry = new THREE.SphereBufferGeometry(this._radius, this._resolution, 
+            this._resolution / 2);
+    }
+}
+
 function pointsToBuffer(points: Array<THREE.Vector3>): LineGeometry {
+    if (points.length < 2) {
+        return new LineGeometry();
+    }
     return new LineGeometry().setPositions(points.reduce((acc, cur) => {
         acc.push(cur.x, cur.y, cur.z);
         return acc;}