diff --git a/daprEnvoy.yaml b/daprEnvoy.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..38aa65e98ded4dcb244f4c904b3c057b2c47520a
--- /dev/null
+++ b/daprEnvoy.yaml
@@ -0,0 +1,39 @@
+static_resources:
+  listeners:
+  - name: listener_0
+    address:
+      socket_address: { address: 0.0.0.0, port_value: 8080 }
+    filter_chains:
+    - filters:
+      - name: envoy.http_connection_manager
+        config:
+          codec_type: auto
+          stat_prefix: ingress_http
+          route_config:
+            name: local_route
+            virtual_hosts:
+            - name: local_service
+              domains: ["*"]
+              routes:
+              - match: { prefix: "/" }
+                route:
+                  cluster: greeter_service
+                  max_grpc_timeout: 0s
+              cors:
+                allow_origin_string_match:
+                - prefix: "*"
+                allow_methods: GET, PUT, DELETE, POST, OPTIONS
+                allow_headers: keep-alive,user-agent,cache-control,content-type,content-transfer-encoding,custom-header-1,x-accept-content-transfer-encoding,x-accept-response-streaming,x-user-agent,x-grpc-web,grpc-timeout
+                max_age: "1728000"
+                expose_headers: custom-header-1,grpc-status,grpc-message
+          http_filters:
+          - name: envoy.grpc_web
+          - name: envoy.cors
+          - name: envoy.router
+  clusters:
+  - name: greeter_service
+    connect_timeout: 0.25s
+    type: logical_dns
+    http2_protocol_options: {}
+    lb_policy: round_robin
+    hosts: [{ socket_address: { address: host.docker.internal, port_value: 50459 }}]
\ No newline at end of file
diff --git a/frontend/README.md b/frontend/README.md
index ff5685bdb95ccb7fb0409e784be97550898cdb9f..ea0c8f87128298cff310f22c8a498030881aa956 100644
--- a/frontend/README.md
+++ b/frontend/README.md
@@ -1,8 +1,12 @@
 FOR THE ENVOY PROXY (needed to translate from http1 to http2 otherwise grpcweb doesn't understand it):
 docker run -d -v <<insert path to directory here>>envoy.yaml:/etc/envoy/envoy.yaml:ro -p 8080:8080 -p 9901:9901 envoyproxy/envoy:v1.15.0
-
+NOTE you have to use complete path (sometimes it complains when using relative paths, no idea why)
 
 We have to run it with dapr so use:
 dapr run --port 5050 --app-id vuefrontend npm run serve
 
-For some reason with vue it cant get the dapr.HTTP environment variable (even though it can with python) so add the port
\ No newline at end of file
+For some reason with vue it cant get the dapr.HTTP environment variable (even though it can with python) so add the port
+
+
+
+docker run -d -v C:\Users\simen\OneDrive\Documents\GitHub\dummy-vue-grpc\envoy.yaml:/etc/envoy/envoy.yaml:ro -p 8080:8080 -p 50459:50459 envoyproxy/envoy:v1.15.0
\ No newline at end of file
diff --git a/frontend/src/App.vue b/frontend/src/App.vue
index 8689cf4d14eff7c001dbe91d11ea60ef11bc08a4..b8c9b57c457c8496fdab9e42858a9447aa4f4408 100644
--- a/frontend/src/App.vue
+++ b/frontend/src/App.vue
@@ -17,7 +17,6 @@ import Navbar from './components/Navbar';
 import gRPCDaprClient from './components/gRPCDaprClient'
 import gRPCclient from './components/gRPCclient'
 import ScatterPlot from './components/ScatterPlot'
-import axios from 'axios'
 
 export default {
   name: 'App',
@@ -27,11 +26,7 @@ export default {
     gRPCDaprClient,
     gRPCclient,
     ScatterPlot  },
-  created(){
-    const daprPort = process.env.DAPR_HTTP_PORT || 5050;
-    const dapr_url = "http://localhost:" + daprPort + "/v1.0/invoke/projector";
-    console.log("Connecting to dapr url: " + dapr_url + "/method/url");
-    console.log(axios.get(dapr_url+ "/method/url"));
+  created(){    
     
   },
   data(){
diff --git a/frontend/src/components/gRPCDaprClient.vue b/frontend/src/components/gRPCDaprClient.vue
index 45af7d0a37bb85487d1772083898953abc4e4459..6ff583d5e55085630ff9e9d4955fc2ed3fddea9d 100644
--- a/frontend/src/components/gRPCDaprClient.vue
+++ b/frontend/src/components/gRPCDaprClient.vue
@@ -1,6 +1,7 @@
 /*eslint-disable*/
 <template>
   <div>
+    <h1>Dapr grpc</h1>
     <h1 class="my-10">Check the developer console for the messages</h1>
 
     <v-layout row>
@@ -14,25 +15,63 @@
 </template>
 
 <script>
-//const { voidNoParam } = require("../generated/point_pb.js");
+const { TrainingSet } = require("../generated/projector_pb");
 //const { PointClient } = require("../generated/point_grpc_web_pb.js");
+const {ProjectorClient} = require("../generated/projector_grpc_web_pb")
+
+import axios from "axios";
 
 export default {
   name: "gRPCDaprClient",
-  data(){
-    return{
-
-    }    
+  data() {
+    return {};
   },
   created: function () {
-    
+    const daprPort = process.env.DAPR_HTTP_PORT || 5051;
+    const dapr_url = "http://localhost:" + daprPort + "/v1.0/invoke/projector";
+
+    var connection = axios.get(dapr_url + "/method/url");
+    console.log(connection);
+    var r = axios.post(dapr_url + "/method/start", 1).then((Response) => {
+      if (Response.data["successful"]) {
+        console.log("Grpc");
+        var connectURL =  "http://" + Response.data["url"];
+        connectURL = "http://192.168.1.34:8080"
+        console.log(connectURL)
+        var client = new ProjectorClient(
+            connectURL,
+          null,
+          null)
+        
+
+        console.log(client);
+        var param = new TrainingSet();
+
+        var stream = client.getProjectionPoints(param);
+
+        stream.on("data", (response) => {
+            console.log("response");
+            console.log(response);
+      });
+      stream.on("error", (err) => {
+      this.errorAlert = true
+      this.succesAlert=false
+      console.log(
+        `Unexpected stream error: code = ${err.code}` +
+          `, message = "${err.message}"`
+      );
+    });
+  }
+      console.log(r);
+
+  })
   },
   methods: {
+    testPrint: function () {
+      console.log("print");
+    },
+    
 
-      testPrint: function(){
-          console.log("print");
-      }
-  }
+  },
 };
-
 </script>
\ No newline at end of file
diff --git a/frontend/src/components/gRPCclient.vue b/frontend/src/components/gRPCclient.vue
index 4ed0a96236dec51b286925b1aacb82d8b13dabcb..161f5a411a2cee9c29650eb528c9c95b275b8340 100644
--- a/frontend/src/components/gRPCclient.vue
+++ b/frontend/src/components/gRPCclient.vue
@@ -16,7 +16,7 @@
       dismissible
       close-icon="mdi-delete"
     >Connected to the server!</v-alert>
-
+  <h1>Dapr</h1>
     <h1 class="my-10">Check the developer console for the messages</h1>
 
     <v-layout row>
diff --git a/frontend/src/generated/projector_grpc_web_pb.js b/frontend/src/generated/projector_grpc_web_pb.js
new file mode 100644
index 0000000000000000000000000000000000000000..c32c1ebdc9cdc2d093d7e2ed8da56053a7f165bb
--- /dev/null
+++ b/frontend/src/generated/projector_grpc_web_pb.js
@@ -0,0 +1,386 @@
+/**
+ * @fileoverview gRPC-Web generated client stub for provee
+ * @enhanceable
+ * @public
+ */
+
+// GENERATED CODE -- DO NOT EDIT!
+
+
+/* eslint-disable */
+// @ts-nocheck
+
+
+
+const grpc = {};
+grpc.web = require('grpc-web');
+
+
+var google_protobuf_empty_pb = require('google-protobuf/google/protobuf/empty_pb.js')
+const proto = {};
+proto.provee = require('./projector_pb.js');
+
+/**
+ * @param {string} hostname
+ * @param {?Object} credentials
+ * @param {?Object} options
+ * @constructor
+ * @struct
+ * @final
+ */
+proto.provee.ProjectorClient =
+    function(hostname, credentials, options) {
+  if (!options) options = {};
+  options['format'] = 'text';
+
+  /**
+   * @private @const {!grpc.web.GrpcWebClientBase} The client
+   */
+  this.client_ = new grpc.web.GrpcWebClientBase(options);
+
+  /**
+   * @private @const {string} The hostname
+   */
+  this.hostname_ = hostname;
+
+};
+
+
+/**
+ * @param {string} hostname
+ * @param {?Object} credentials
+ * @param {?Object} options
+ * @constructor
+ * @struct
+ * @final
+ */
+proto.provee.ProjectorPromiseClient =
+    function(hostname, credentials, options) {
+  if (!options) options = {};
+  options['format'] = 'text';
+
+  /**
+   * @private @const {!grpc.web.GrpcWebClientBase} The client
+   */
+  this.client_ = new grpc.web.GrpcWebClientBase(options);
+
+  /**
+   * @private @const {string} The hostname
+   */
+  this.hostname_ = hostname;
+
+};
+
+
+/**
+ * @const
+ * @type {!grpc.web.MethodDescriptor<
+ *   !proto.google.protobuf.Empty,
+ *   !proto.google.protobuf.Empty>}
+ */
+const methodDescriptor_Projector_Start = new grpc.web.MethodDescriptor(
+  '/provee.Projector/Start',
+  grpc.web.MethodType.UNARY,
+  google_protobuf_empty_pb.Empty,
+  google_protobuf_empty_pb.Empty,
+  /**
+   * @param {!proto.google.protobuf.Empty} request
+   * @return {!Uint8Array}
+   */
+  function(request) {
+    return request.serializeBinary();
+  },
+  google_protobuf_empty_pb.Empty.deserializeBinary
+);
+
+
+/**
+ * @const
+ * @type {!grpc.web.AbstractClientBase.MethodInfo<
+ *   !proto.google.protobuf.Empty,
+ *   !proto.google.protobuf.Empty>}
+ */
+const methodInfo_Projector_Start = new grpc.web.AbstractClientBase.MethodInfo(
+  google_protobuf_empty_pb.Empty,
+  /**
+   * @param {!proto.google.protobuf.Empty} request
+   * @return {!Uint8Array}
+   */
+  function(request) {
+    return request.serializeBinary();
+  },
+  google_protobuf_empty_pb.Empty.deserializeBinary
+);
+
+
+/**
+ * @param {!proto.google.protobuf.Empty} request The
+ *     request proto
+ * @param {?Object<string, string>} metadata User defined
+ *     call metadata
+ * @param {function(?grpc.web.Error, ?proto.google.protobuf.Empty)}
+ *     callback The callback function(error, response)
+ * @return {!grpc.web.ClientReadableStream<!proto.google.protobuf.Empty>|undefined}
+ *     The XHR Node Readable Stream
+ */
+proto.provee.ProjectorClient.prototype.start =
+    function(request, metadata, callback) {
+  return this.client_.rpcCall(this.hostname_ +
+      '/provee.Projector/Start',
+      request,
+      metadata || {},
+      methodDescriptor_Projector_Start,
+      callback);
+};
+
+
+/**
+ * @param {!proto.google.protobuf.Empty} request The
+ *     request proto
+ * @param {?Object<string, string>} metadata User defined
+ *     call metadata
+ * @return {!Promise<!proto.google.protobuf.Empty>}
+ *     Promise that resolves to the response
+ */
+proto.provee.ProjectorPromiseClient.prototype.start =
+    function(request, metadata) {
+  return this.client_.unaryCall(this.hostname_ +
+      '/provee.Projector/Start',
+      request,
+      metadata || {},
+      methodDescriptor_Projector_Start);
+};
+
+
+/**
+ * @const
+ * @type {!grpc.web.MethodDescriptor<
+ *   !proto.google.protobuf.Empty,
+ *   !proto.google.protobuf.Empty>}
+ */
+const methodDescriptor_Projector_Stop = new grpc.web.MethodDescriptor(
+  '/provee.Projector/Stop',
+  grpc.web.MethodType.UNARY,
+  google_protobuf_empty_pb.Empty,
+  google_protobuf_empty_pb.Empty,
+  /**
+   * @param {!proto.google.protobuf.Empty} request
+   * @return {!Uint8Array}
+   */
+  function(request) {
+    return request.serializeBinary();
+  },
+  google_protobuf_empty_pb.Empty.deserializeBinary
+);
+
+
+/**
+ * @const
+ * @type {!grpc.web.AbstractClientBase.MethodInfo<
+ *   !proto.google.protobuf.Empty,
+ *   !proto.google.protobuf.Empty>}
+ */
+const methodInfo_Projector_Stop = new grpc.web.AbstractClientBase.MethodInfo(
+  google_protobuf_empty_pb.Empty,
+  /**
+   * @param {!proto.google.protobuf.Empty} request
+   * @return {!Uint8Array}
+   */
+  function(request) {
+    return request.serializeBinary();
+  },
+  google_protobuf_empty_pb.Empty.deserializeBinary
+);
+
+
+/**
+ * @param {!proto.google.protobuf.Empty} request The
+ *     request proto
+ * @param {?Object<string, string>} metadata User defined
+ *     call metadata
+ * @param {function(?grpc.web.Error, ?proto.google.protobuf.Empty)}
+ *     callback The callback function(error, response)
+ * @return {!grpc.web.ClientReadableStream<!proto.google.protobuf.Empty>|undefined}
+ *     The XHR Node Readable Stream
+ */
+proto.provee.ProjectorClient.prototype.stop =
+    function(request, metadata, callback) {
+  return this.client_.rpcCall(this.hostname_ +
+      '/provee.Projector/Stop',
+      request,
+      metadata || {},
+      methodDescriptor_Projector_Stop,
+      callback);
+};
+
+
+/**
+ * @param {!proto.google.protobuf.Empty} request The
+ *     request proto
+ * @param {?Object<string, string>} metadata User defined
+ *     call metadata
+ * @return {!Promise<!proto.google.protobuf.Empty>}
+ *     Promise that resolves to the response
+ */
+proto.provee.ProjectorPromiseClient.prototype.stop =
+    function(request, metadata) {
+  return this.client_.unaryCall(this.hostname_ +
+      '/provee.Projector/Stop',
+      request,
+      metadata || {},
+      methodDescriptor_Projector_Stop);
+};
+
+
+/**
+ * @const
+ * @type {!grpc.web.MethodDescriptor<
+ *   !proto.provee.TrainingSet,
+ *   !proto.provee.Point>}
+ */
+const methodDescriptor_Projector_GetProjectionPoints = new grpc.web.MethodDescriptor(
+  '/provee.Projector/GetProjectionPoints',
+  grpc.web.MethodType.SERVER_STREAMING,
+  proto.provee.TrainingSet,
+  proto.provee.Point,
+  /**
+   * @param {!proto.provee.TrainingSet} request
+   * @return {!Uint8Array}
+   */
+  function(request) {
+    return request.serializeBinary();
+  },
+  proto.provee.Point.deserializeBinary
+);
+
+
+/**
+ * @const
+ * @type {!grpc.web.AbstractClientBase.MethodInfo<
+ *   !proto.provee.TrainingSet,
+ *   !proto.provee.Point>}
+ */
+const methodInfo_Projector_GetProjectionPoints = new grpc.web.AbstractClientBase.MethodInfo(
+  proto.provee.Point,
+  /**
+   * @param {!proto.provee.TrainingSet} request
+   * @return {!Uint8Array}
+   */
+  function(request) {
+    return request.serializeBinary();
+  },
+  proto.provee.Point.deserializeBinary
+);
+
+
+/**
+ * @param {!proto.provee.TrainingSet} request The request proto
+ * @param {?Object<string, string>} metadata User defined
+ *     call metadata
+ * @return {!grpc.web.ClientReadableStream<!proto.provee.Point>}
+ *     The XHR Node Readable Stream
+ */
+proto.provee.ProjectorClient.prototype.getProjectionPoints =
+    function(request, metadata) {
+  return this.client_.serverStreaming(this.hostname_ +
+      '/provee.Projector/GetProjectionPoints',
+      request,
+      metadata || {},
+      methodDescriptor_Projector_GetProjectionPoints);
+};
+
+
+/**
+ * @param {!proto.provee.TrainingSet} request The request proto
+ * @param {?Object<string, string>} metadata User defined
+ *     call metadata
+ * @return {!grpc.web.ClientReadableStream<!proto.provee.Point>}
+ *     The XHR Node Readable Stream
+ */
+proto.provee.ProjectorPromiseClient.prototype.getProjectionPoints =
+    function(request, metadata) {
+  return this.client_.serverStreaming(this.hostname_ +
+      '/provee.Projector/GetProjectionPoints',
+      request,
+      metadata || {},
+      methodDescriptor_Projector_GetProjectionPoints);
+};
+
+
+/**
+ * @const
+ * @type {!grpc.web.MethodDescriptor<
+ *   !proto.google.protobuf.Empty,
+ *   !proto.provee.Point>}
+ */
+const methodDescriptor_Projector_GetUpdates = new grpc.web.MethodDescriptor(
+  '/provee.Projector/GetUpdates',
+  grpc.web.MethodType.SERVER_STREAMING,
+  google_protobuf_empty_pb.Empty,
+  proto.provee.Point,
+  /**
+   * @param {!proto.google.protobuf.Empty} request
+   * @return {!Uint8Array}
+   */
+  function(request) {
+    return request.serializeBinary();
+  },
+  proto.provee.Point.deserializeBinary
+);
+
+
+/**
+ * @const
+ * @type {!grpc.web.AbstractClientBase.MethodInfo<
+ *   !proto.google.protobuf.Empty,
+ *   !proto.provee.Point>}
+ */
+const methodInfo_Projector_GetUpdates = new grpc.web.AbstractClientBase.MethodInfo(
+  proto.provee.Point,
+  /**
+   * @param {!proto.google.protobuf.Empty} request
+   * @return {!Uint8Array}
+   */
+  function(request) {
+    return request.serializeBinary();
+  },
+  proto.provee.Point.deserializeBinary
+);
+
+
+/**
+ * @param {!proto.google.protobuf.Empty} request The request proto
+ * @param {?Object<string, string>} metadata User defined
+ *     call metadata
+ * @return {!grpc.web.ClientReadableStream<!proto.provee.Point>}
+ *     The XHR Node Readable Stream
+ */
+proto.provee.ProjectorClient.prototype.getUpdates =
+    function(request, metadata) {
+  return this.client_.serverStreaming(this.hostname_ +
+      '/provee.Projector/GetUpdates',
+      request,
+      metadata || {},
+      methodDescriptor_Projector_GetUpdates);
+};
+
+
+/**
+ * @param {!proto.google.protobuf.Empty} request The request proto
+ * @param {?Object<string, string>} metadata User defined
+ *     call metadata
+ * @return {!grpc.web.ClientReadableStream<!proto.provee.Point>}
+ *     The XHR Node Readable Stream
+ */
+proto.provee.ProjectorPromiseClient.prototype.getUpdates =
+    function(request, metadata) {
+  return this.client_.serverStreaming(this.hostname_ +
+      '/provee.Projector/GetUpdates',
+      request,
+      metadata || {},
+      methodDescriptor_Projector_GetUpdates);
+};
+
+
+module.exports = proto.provee;
+
diff --git a/frontend/src/generated/projector_pb.js b/frontend/src/generated/projector_pb.js
new file mode 100644
index 0000000000000000000000000000000000000000..3f6d027b2351931711e4fe59769184eb277ff91f
--- /dev/null
+++ b/frontend/src/generated/projector_pb.js
@@ -0,0 +1,650 @@
+// source: src/projector.proto
+/**
+ * @fileoverview
+ * @enhanceable
+ * @suppress {messageConventions} JS Compiler reports an error if a variable or
+ *     field starts with 'MSG_' and isn't a translatable message.
+ * @public
+ */
+// GENERATED CODE -- DO NOT EDIT!
+/* eslint-disable */
+
+var jspb = require('google-protobuf');
+var goog = jspb;
+var global = Function('return this')();
+
+var google_protobuf_empty_pb = require('google-protobuf/google/protobuf/empty_pb.js');
+goog.object.extend(proto, google_protobuf_empty_pb);
+goog.exportSymbol('proto.provee.Point', null, global);
+goog.exportSymbol('proto.provee.TrainingSet', null, global);
+goog.exportSymbol('proto.provee.TrainingSetRow', null, global);
+/**
+ * Generated by JsPbCodeGenerator.
+ * @param {Array=} opt_data Optional initial data array, typically from a
+ * server response, or constructed directly in Javascript. The array is used
+ * in place and becomes part of the constructed object. It is not cloned.
+ * If no data is provided, the constructed object will be empty, but still
+ * valid.
+ * @extends {jspb.Message}
+ * @constructor
+ */
+proto.provee.Point = function(opt_data) {
+  jspb.Message.initialize(this, opt_data, 0, -1, null, null);
+};
+goog.inherits(proto.provee.Point, jspb.Message);
+if (goog.DEBUG && !COMPILED) {
+  /**
+   * @public
+   * @override
+   */
+  proto.provee.Point.displayName = 'proto.provee.Point';
+}
+/**
+ * Generated by JsPbCodeGenerator.
+ * @param {Array=} opt_data Optional initial data array, typically from a
+ * server response, or constructed directly in Javascript. The array is used
+ * in place and becomes part of the constructed object. It is not cloned.
+ * If no data is provided, the constructed object will be empty, but still
+ * valid.
+ * @extends {jspb.Message}
+ * @constructor
+ */
+proto.provee.TrainingSet = function(opt_data) {
+  jspb.Message.initialize(this, opt_data, 0, -1, proto.provee.TrainingSet.repeatedFields_, null);
+};
+goog.inherits(proto.provee.TrainingSet, jspb.Message);
+if (goog.DEBUG && !COMPILED) {
+  /**
+   * @public
+   * @override
+   */
+  proto.provee.TrainingSet.displayName = 'proto.provee.TrainingSet';
+}
+/**
+ * Generated by JsPbCodeGenerator.
+ * @param {Array=} opt_data Optional initial data array, typically from a
+ * server response, or constructed directly in Javascript. The array is used
+ * in place and becomes part of the constructed object. It is not cloned.
+ * If no data is provided, the constructed object will be empty, but still
+ * valid.
+ * @extends {jspb.Message}
+ * @constructor
+ */
+proto.provee.TrainingSetRow = function(opt_data) {
+  jspb.Message.initialize(this, opt_data, 0, -1, proto.provee.TrainingSetRow.repeatedFields_, null);
+};
+goog.inherits(proto.provee.TrainingSetRow, jspb.Message);
+if (goog.DEBUG && !COMPILED) {
+  /**
+   * @public
+   * @override
+   */
+  proto.provee.TrainingSetRow.displayName = 'proto.provee.TrainingSetRow';
+}
+
+
+
+if (jspb.Message.GENERATE_TO_OBJECT) {
+/**
+ * Creates an object representation of this proto.
+ * Field names that are reserved in JavaScript and will be renamed to pb_name.
+ * Optional fields that are not set will be set to undefined.
+ * To access a reserved field use, foo.pb_<name>, eg, foo.pb_default.
+ * For the list of reserved names please see:
+ *     net/proto2/compiler/js/internal/generator.cc#kKeyword.
+ * @param {boolean=} opt_includeInstance Deprecated. whether to include the
+ *     JSPB instance for transitional soy proto support:
+ *     http://goto/soy-param-migration
+ * @return {!Object}
+ */
+proto.provee.Point.prototype.toObject = function(opt_includeInstance) {
+  return proto.provee.Point.toObject(opt_includeInstance, this);
+};
+
+
+/**
+ * Static version of the {@see toObject} method.
+ * @param {boolean|undefined} includeInstance Deprecated. Whether to include
+ *     the JSPB instance for transitional soy proto support:
+ *     http://goto/soy-param-migration
+ * @param {!proto.provee.Point} msg The msg instance to transform.
+ * @return {!Object}
+ * @suppress {unusedLocalVariables} f is only used for nested messages
+ */
+proto.provee.Point.toObject = function(includeInstance, msg) {
+  var f, obj = {
+    id: jspb.Message.getFieldWithDefault(msg, 1, 0),
+    x: jspb.Message.getFieldWithDefault(msg, 2, 0),
+    y: jspb.Message.getFieldWithDefault(msg, 3, 0)
+  };
+
+  if (includeInstance) {
+    obj.$jspbMessageInstance = msg;
+  }
+  return obj;
+};
+}
+
+
+/**
+ * Deserializes binary data (in protobuf wire format).
+ * @param {jspb.ByteSource} bytes The bytes to deserialize.
+ * @return {!proto.provee.Point}
+ */
+proto.provee.Point.deserializeBinary = function(bytes) {
+  var reader = new jspb.BinaryReader(bytes);
+  var msg = new proto.provee.Point;
+  return proto.provee.Point.deserializeBinaryFromReader(msg, reader);
+};
+
+
+/**
+ * Deserializes binary data (in protobuf wire format) from the
+ * given reader into the given message object.
+ * @param {!proto.provee.Point} msg The message object to deserialize into.
+ * @param {!jspb.BinaryReader} reader The BinaryReader to use.
+ * @return {!proto.provee.Point}
+ */
+proto.provee.Point.deserializeBinaryFromReader = function(msg, reader) {
+  while (reader.nextField()) {
+    if (reader.isEndGroup()) {
+      break;
+    }
+    var field = reader.getFieldNumber();
+    switch (field) {
+    case 1:
+      var value = /** @type {number} */ (reader.readInt32());
+      msg.setId(value);
+      break;
+    case 2:
+      var value = /** @type {number} */ (reader.readInt32());
+      msg.setX(value);
+      break;
+    case 3:
+      var value = /** @type {number} */ (reader.readInt32());
+      msg.setY(value);
+      break;
+    default:
+      reader.skipField();
+      break;
+    }
+  }
+  return msg;
+};
+
+
+/**
+ * Serializes the message to binary data (in protobuf wire format).
+ * @return {!Uint8Array}
+ */
+proto.provee.Point.prototype.serializeBinary = function() {
+  var writer = new jspb.BinaryWriter();
+  proto.provee.Point.serializeBinaryToWriter(this, writer);
+  return writer.getResultBuffer();
+};
+
+
+/**
+ * Serializes the given message to binary data (in protobuf wire
+ * format), writing to the given BinaryWriter.
+ * @param {!proto.provee.Point} message
+ * @param {!jspb.BinaryWriter} writer
+ * @suppress {unusedLocalVariables} f is only used for nested messages
+ */
+proto.provee.Point.serializeBinaryToWriter = function(message, writer) {
+  var f = undefined;
+  f = message.getId();
+  if (f !== 0) {
+    writer.writeInt32(
+      1,
+      f
+    );
+  }
+  f = message.getX();
+  if (f !== 0) {
+    writer.writeInt32(
+      2,
+      f
+    );
+  }
+  f = message.getY();
+  if (f !== 0) {
+    writer.writeInt32(
+      3,
+      f
+    );
+  }
+};
+
+
+/**
+ * optional int32 id = 1;
+ * @return {number}
+ */
+proto.provee.Point.prototype.getId = function() {
+  return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 1, 0));
+};
+
+
+/**
+ * @param {number} value
+ * @return {!proto.provee.Point} returns this
+ */
+proto.provee.Point.prototype.setId = function(value) {
+  return jspb.Message.setProto3IntField(this, 1, value);
+};
+
+
+/**
+ * optional int32 x = 2;
+ * @return {number}
+ */
+proto.provee.Point.prototype.getX = function() {
+  return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 2, 0));
+};
+
+
+/**
+ * @param {number} value
+ * @return {!proto.provee.Point} returns this
+ */
+proto.provee.Point.prototype.setX = function(value) {
+  return jspb.Message.setProto3IntField(this, 2, value);
+};
+
+
+/**
+ * optional int32 y = 3;
+ * @return {number}
+ */
+proto.provee.Point.prototype.getY = function() {
+  return /** @type {number} */ (jspb.Message.getFieldWithDefault(this, 3, 0));
+};
+
+
+/**
+ * @param {number} value
+ * @return {!proto.provee.Point} returns this
+ */
+proto.provee.Point.prototype.setY = function(value) {
+  return jspb.Message.setProto3IntField(this, 3, value);
+};
+
+
+
+/**
+ * List of repeated fields within this message type.
+ * @private {!Array<number>}
+ * @const
+ */
+proto.provee.TrainingSet.repeatedFields_ = [2];
+
+
+
+if (jspb.Message.GENERATE_TO_OBJECT) {
+/**
+ * Creates an object representation of this proto.
+ * Field names that are reserved in JavaScript and will be renamed to pb_name.
+ * Optional fields that are not set will be set to undefined.
+ * To access a reserved field use, foo.pb_<name>, eg, foo.pb_default.
+ * For the list of reserved names please see:
+ *     net/proto2/compiler/js/internal/generator.cc#kKeyword.
+ * @param {boolean=} opt_includeInstance Deprecated. whether to include the
+ *     JSPB instance for transitional soy proto support:
+ *     http://goto/soy-param-migration
+ * @return {!Object}
+ */
+proto.provee.TrainingSet.prototype.toObject = function(opt_includeInstance) {
+  return proto.provee.TrainingSet.toObject(opt_includeInstance, this);
+};
+
+
+/**
+ * Static version of the {@see toObject} method.
+ * @param {boolean|undefined} includeInstance Deprecated. Whether to include
+ *     the JSPB instance for transitional soy proto support:
+ *     http://goto/soy-param-migration
+ * @param {!proto.provee.TrainingSet} msg The msg instance to transform.
+ * @return {!Object}
+ * @suppress {unusedLocalVariables} f is only used for nested messages
+ */
+proto.provee.TrainingSet.toObject = function(includeInstance, msg) {
+  var f, obj = {
+    modelid: jspb.Message.getFieldWithDefault(msg, 1, ""),
+    rowsList: jspb.Message.toObjectList(msg.getRowsList(),
+    proto.provee.TrainingSetRow.toObject, includeInstance)
+  };
+
+  if (includeInstance) {
+    obj.$jspbMessageInstance = msg;
+  }
+  return obj;
+};
+}
+
+
+/**
+ * Deserializes binary data (in protobuf wire format).
+ * @param {jspb.ByteSource} bytes The bytes to deserialize.
+ * @return {!proto.provee.TrainingSet}
+ */
+proto.provee.TrainingSet.deserializeBinary = function(bytes) {
+  var reader = new jspb.BinaryReader(bytes);
+  var msg = new proto.provee.TrainingSet;
+  return proto.provee.TrainingSet.deserializeBinaryFromReader(msg, reader);
+};
+
+
+/**
+ * Deserializes binary data (in protobuf wire format) from the
+ * given reader into the given message object.
+ * @param {!proto.provee.TrainingSet} msg The message object to deserialize into.
+ * @param {!jspb.BinaryReader} reader The BinaryReader to use.
+ * @return {!proto.provee.TrainingSet}
+ */
+proto.provee.TrainingSet.deserializeBinaryFromReader = function(msg, reader) {
+  while (reader.nextField()) {
+    if (reader.isEndGroup()) {
+      break;
+    }
+    var field = reader.getFieldNumber();
+    switch (field) {
+    case 1:
+      var value = /** @type {string} */ (reader.readString());
+      msg.setModelid(value);
+      break;
+    case 2:
+      var value = new proto.provee.TrainingSetRow;
+      reader.readMessage(value,proto.provee.TrainingSetRow.deserializeBinaryFromReader);
+      msg.addRows(value);
+      break;
+    default:
+      reader.skipField();
+      break;
+    }
+  }
+  return msg;
+};
+
+
+/**
+ * Serializes the message to binary data (in protobuf wire format).
+ * @return {!Uint8Array}
+ */
+proto.provee.TrainingSet.prototype.serializeBinary = function() {
+  var writer = new jspb.BinaryWriter();
+  proto.provee.TrainingSet.serializeBinaryToWriter(this, writer);
+  return writer.getResultBuffer();
+};
+
+
+/**
+ * Serializes the given message to binary data (in protobuf wire
+ * format), writing to the given BinaryWriter.
+ * @param {!proto.provee.TrainingSet} message
+ * @param {!jspb.BinaryWriter} writer
+ * @suppress {unusedLocalVariables} f is only used for nested messages
+ */
+proto.provee.TrainingSet.serializeBinaryToWriter = function(message, writer) {
+  var f = undefined;
+  f = message.getModelid();
+  if (f.length > 0) {
+    writer.writeString(
+      1,
+      f
+    );
+  }
+  f = message.getRowsList();
+  if (f.length > 0) {
+    writer.writeRepeatedMessage(
+      2,
+      f,
+      proto.provee.TrainingSetRow.serializeBinaryToWriter
+    );
+  }
+};
+
+
+/**
+ * optional string modelid = 1;
+ * @return {string}
+ */
+proto.provee.TrainingSet.prototype.getModelid = function() {
+  return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 1, ""));
+};
+
+
+/**
+ * @param {string} value
+ * @return {!proto.provee.TrainingSet} returns this
+ */
+proto.provee.TrainingSet.prototype.setModelid = function(value) {
+  return jspb.Message.setProto3StringField(this, 1, value);
+};
+
+
+/**
+ * repeated TrainingSetRow rows = 2;
+ * @return {!Array<!proto.provee.TrainingSetRow>}
+ */
+proto.provee.TrainingSet.prototype.getRowsList = function() {
+  return /** @type{!Array<!proto.provee.TrainingSetRow>} */ (
+    jspb.Message.getRepeatedWrapperField(this, proto.provee.TrainingSetRow, 2));
+};
+
+
+/**
+ * @param {!Array<!proto.provee.TrainingSetRow>} value
+ * @return {!proto.provee.TrainingSet} returns this
+*/
+proto.provee.TrainingSet.prototype.setRowsList = function(value) {
+  return jspb.Message.setRepeatedWrapperField(this, 2, value);
+};
+
+
+/**
+ * @param {!proto.provee.TrainingSetRow=} opt_value
+ * @param {number=} opt_index
+ * @return {!proto.provee.TrainingSetRow}
+ */
+proto.provee.TrainingSet.prototype.addRows = function(opt_value, opt_index) {
+  return jspb.Message.addToRepeatedWrapperField(this, 2, opt_value, proto.provee.TrainingSetRow, opt_index);
+};
+
+
+/**
+ * Clears the list making it empty but non-null.
+ * @return {!proto.provee.TrainingSet} returns this
+ */
+proto.provee.TrainingSet.prototype.clearRowsList = function() {
+  return this.setRowsList([]);
+};
+
+
+
+/**
+ * List of repeated fields within this message type.
+ * @private {!Array<number>}
+ * @const
+ */
+proto.provee.TrainingSetRow.repeatedFields_ = [2];
+
+
+
+if (jspb.Message.GENERATE_TO_OBJECT) {
+/**
+ * Creates an object representation of this proto.
+ * Field names that are reserved in JavaScript and will be renamed to pb_name.
+ * Optional fields that are not set will be set to undefined.
+ * To access a reserved field use, foo.pb_<name>, eg, foo.pb_default.
+ * For the list of reserved names please see:
+ *     net/proto2/compiler/js/internal/generator.cc#kKeyword.
+ * @param {boolean=} opt_includeInstance Deprecated. whether to include the
+ *     JSPB instance for transitional soy proto support:
+ *     http://goto/soy-param-migration
+ * @return {!Object}
+ */
+proto.provee.TrainingSetRow.prototype.toObject = function(opt_includeInstance) {
+  return proto.provee.TrainingSetRow.toObject(opt_includeInstance, this);
+};
+
+
+/**
+ * Static version of the {@see toObject} method.
+ * @param {boolean|undefined} includeInstance Deprecated. Whether to include
+ *     the JSPB instance for transitional soy proto support:
+ *     http://goto/soy-param-migration
+ * @param {!proto.provee.TrainingSetRow} msg The msg instance to transform.
+ * @return {!Object}
+ * @suppress {unusedLocalVariables} f is only used for nested messages
+ */
+proto.provee.TrainingSetRow.toObject = function(includeInstance, msg) {
+  var f, obj = {
+    id: jspb.Message.getFieldWithDefault(msg, 1, ""),
+    hdvectorList: (f = jspb.Message.getRepeatedFloatingPointField(msg, 2)) == null ? undefined : f
+  };
+
+  if (includeInstance) {
+    obj.$jspbMessageInstance = msg;
+  }
+  return obj;
+};
+}
+
+
+/**
+ * Deserializes binary data (in protobuf wire format).
+ * @param {jspb.ByteSource} bytes The bytes to deserialize.
+ * @return {!proto.provee.TrainingSetRow}
+ */
+proto.provee.TrainingSetRow.deserializeBinary = function(bytes) {
+  var reader = new jspb.BinaryReader(bytes);
+  var msg = new proto.provee.TrainingSetRow;
+  return proto.provee.TrainingSetRow.deserializeBinaryFromReader(msg, reader);
+};
+
+
+/**
+ * Deserializes binary data (in protobuf wire format) from the
+ * given reader into the given message object.
+ * @param {!proto.provee.TrainingSetRow} msg The message object to deserialize into.
+ * @param {!jspb.BinaryReader} reader The BinaryReader to use.
+ * @return {!proto.provee.TrainingSetRow}
+ */
+proto.provee.TrainingSetRow.deserializeBinaryFromReader = function(msg, reader) {
+  while (reader.nextField()) {
+    if (reader.isEndGroup()) {
+      break;
+    }
+    var field = reader.getFieldNumber();
+    switch (field) {
+    case 1:
+      var value = /** @type {string} */ (reader.readString());
+      msg.setId(value);
+      break;
+    case 2:
+      var value = /** @type {!Array<number>} */ (reader.readPackedDouble());
+      msg.setHdvectorList(value);
+      break;
+    default:
+      reader.skipField();
+      break;
+    }
+  }
+  return msg;
+};
+
+
+/**
+ * Serializes the message to binary data (in protobuf wire format).
+ * @return {!Uint8Array}
+ */
+proto.provee.TrainingSetRow.prototype.serializeBinary = function() {
+  var writer = new jspb.BinaryWriter();
+  proto.provee.TrainingSetRow.serializeBinaryToWriter(this, writer);
+  return writer.getResultBuffer();
+};
+
+
+/**
+ * Serializes the given message to binary data (in protobuf wire
+ * format), writing to the given BinaryWriter.
+ * @param {!proto.provee.TrainingSetRow} message
+ * @param {!jspb.BinaryWriter} writer
+ * @suppress {unusedLocalVariables} f is only used for nested messages
+ */
+proto.provee.TrainingSetRow.serializeBinaryToWriter = function(message, writer) {
+  var f = undefined;
+  f = message.getId();
+  if (f.length > 0) {
+    writer.writeString(
+      1,
+      f
+    );
+  }
+  f = message.getHdvectorList();
+  if (f.length > 0) {
+    writer.writePackedDouble(
+      2,
+      f
+    );
+  }
+};
+
+
+/**
+ * optional string id = 1;
+ * @return {string}
+ */
+proto.provee.TrainingSetRow.prototype.getId = function() {
+  return /** @type {string} */ (jspb.Message.getFieldWithDefault(this, 1, ""));
+};
+
+
+/**
+ * @param {string} value
+ * @return {!proto.provee.TrainingSetRow} returns this
+ */
+proto.provee.TrainingSetRow.prototype.setId = function(value) {
+  return jspb.Message.setProto3StringField(this, 1, value);
+};
+
+
+/**
+ * repeated double hdvector = 2;
+ * @return {!Array<number>}
+ */
+proto.provee.TrainingSetRow.prototype.getHdvectorList = function() {
+  return /** @type {!Array<number>} */ (jspb.Message.getRepeatedFloatingPointField(this, 2));
+};
+
+
+/**
+ * @param {!Array<number>} value
+ * @return {!proto.provee.TrainingSetRow} returns this
+ */
+proto.provee.TrainingSetRow.prototype.setHdvectorList = function(value) {
+  return jspb.Message.setField(this, 2, value || []);
+};
+
+
+/**
+ * @param {number} value
+ * @param {number=} opt_index
+ * @return {!proto.provee.TrainingSetRow} returns this
+ */
+proto.provee.TrainingSetRow.prototype.addHdvector = function(value, opt_index) {
+  return jspb.Message.addToRepeatedField(this, 2, value, opt_index);
+};
+
+
+/**
+ * Clears the list making it empty but non-null.
+ * @return {!proto.provee.TrainingSetRow} returns this
+ */
+proto.provee.TrainingSetRow.prototype.clearHdvectorList = function() {
+  return this.setHdvectorList([]);
+};
+
+
+goog.object.extend(exports, proto.provee);
diff --git a/frontend/src/projector.proto b/frontend/src/projector.proto
new file mode 100644
index 0000000000000000000000000000000000000000..6cd0b79246de2fd8b01f895619055cc226317567
--- /dev/null
+++ b/frontend/src/projector.proto
@@ -0,0 +1,71 @@
+syntax = "proto3";
+
+package provee;
+
+import "google/protobuf/empty.proto";
+
+option java_multiple_files = true;
+option java_package = "nl.uuvig.provee";
+option java_outer_classname = "ProveProjectorGRCP";
+option objc_class_prefix = "PROVEE";
+
+
+// Interface exported by the server.
+service Projector {
+  // A simple RPC.
+  //
+  // Start the Projector calculation
+  rpc Start(google.protobuf.Empty) returns (google.protobuf.Empty);
+
+  // Stop the Projector calculation
+  rpc Stop(google.protobuf.Empty) returns (google.protobuf.Empty);
+
+  // A server-to-client streaming RPC.
+  //
+  // Obtains the Projection Points in 2D given the trainings values stored in a row format.  Results are
+  // streamed rather than returned at once (e.g. in a response message with a
+  // repeated field).
+  rpc GetProjectionPoints(TrainingSet) returns (stream Point) {}
+
+  // A server-to-client streaming RPC.
+  //
+  // Obtains the Projection Points in 2D given the trainings values stored in a row format.  Results are
+  // streamed rather than returned at once (e.g. in a response message with a
+  // repeated field).
+  rpc GetUpdates(google.protobuf.Empty) returns (stream Point) {}
+
+//   // A client-to-server streaming RPC.
+//   //
+//   // Accepts a stream of Points on a route being traversed, returning a
+//   // RouteSummary when traversal is completed.
+//   rpc RecordRoute(stream Point) returns (RouteSummary) {}
+
+//   // A Bidirectional streaming RPC.
+//   //
+//   // Accepts a stream of RouteNotes sent while a route is being traversed,
+//   // while receiving other RouteNotes (e.g. from other users).
+//   rpc RouteChat(stream RouteNote) returns (stream RouteNote) {}
+}
+
+// Points are represented as x-y pairs
+message Point {
+  int32 id = 1;
+  int32 x = 2;
+  int32 y = 3;
+}
+
+// Needs description
+message TrainingSet {
+  string modelid = 1;
+  repeated TrainingSetRow rows = 2;
+}
+
+// Needs documentation
+message TrainingSetRow {
+  // The id of the row, e.g., row index.
+  string id = 1;
+
+  // The hd vector of the item.
+  repeated double hdvector = 2 [packed=true];
+}
+