# Introduction

LampixJS is the official JavaScript library for creating [Lampix](https://lampix.com/) applications, making use of the internal API Lampix devices expose.

## Application considerations

Any valid web application can run on Lampix. The only thing to keep in mind is that **Lampix will not, by default, make things interactive**.\
To integrate with Lampix, use the [lampixjs](https://www.npmjs.com/package/@lampix/core) library. The library is meant to be used inside the context of a Lampix device or the Lampix simulator. Loaded inside an environment without the expected API will simply make the library search for the API indefinitely, rendering its most relevant methods unusable.

### What web technologies can I use?

You can create your application any way you see fit, provided the end result respects what is outlined in the [application structure](/application-development/deploying/application-structure).\
That said, you can use:

* [React](https://reactjs.org/)
* [Angular](https://angular.io/)
* [Vue](https://vuejs.org/)
* [VanillaJS](http://vanilla-js.com/) - as in no libraries, if you'd prefer not using any
* and any other technology used to develop web applications

### Do I need to worry about browser support?

You only need to worry about Chromium v66. Search this [compatibility table](https://caniuse.com/#compare=chrome+66) for the functionality you need.\
Chromium v66 is quite advanced, and includes features such as the [fetch API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) and [web animations API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Animations_API) out of the box (the latter, for instance, is an experimental technology whose `animation.finished` promises show issues, but can easily be mitigated with the properly working `animation.onfinish` callbacks).


# Getting Started

* [Up and Running](/application-development/getting-started/up-and-running)
* [Boilerplate](/application-development/getting-started/boilerplate)


# Up and Running

This guide is a short introduction into running an application inside the simulator. For a more detailed overview on how to create an application for Lampix, see the [step by step application](/application-development/step-by-step) guide.

## Prerequisites

* Node.js and NPM (via [installer](https://nodejs.org/en/), recommended for Windows, or via [node version manager](https://github.com/creationix/nvm), recommended for Linux and macOS)

## Download

* [Windows](https://s3.amazonaws.com/simulator.lampix.com/lampix-simulator-master.exe)
* [macOS](https://s3.amazonaws.com/simulator.lampix.com/lampix-simulator-master.dmg)
* [Linux](https://s3.amazonaws.com/simulator.lampix.com/lampix-simulator-master.AppImage)

### Linux only

* `chmod +x <path-to-AppImage>`
* Run

## Using the sample app

```bash
git clone https://github.com/lampix-org/minimal-sample.git
cd minimal-sample
npm install
npm start
```

This will start a development server at `http://localhost:8080`, provided that port is free to use. Otherwise, check the terminal output to find out the URL to go to.

## Loading it inside the simulator

* Start the simulator
* Enter the URL in the address bar at the top of the simulator
* Press *Enter* or the *Load* button

If the URL is valid, a new window with the simulated app will open.

## Selecting watchers and the recognized class

The sample app uses finger detection, which corresponds to `NeuralNetworkClassifier` as the watcher name, with `0` as no finger detected and `1` as finger detected.

Steps:

* in the main window (the one used to load apps), open the expansion panel in the middle that says `Simulator <your-url>`
* select `NeuralNetworkClassifier` as the watcher name
* select `1` as the recognized class
* in the simulated app window, click the "Increase Count" button

Note that you will only be able to use `1` once until you use `0` to deactivate the button.


# Boilerplate

Any modern JavaScript boilerplate (aka "template", "starter", "seed") is fine, and one can develop Lampix applications without any boilerplate.\
The following are all equally fine:

* [create-react-app](https://github.com/facebook/create-react-app)
* [angular-cli](https://github.com/angular/angular-cli)
* [vue-cli](https://github.com/vuejs/vue-cli)

Depending on your application's needs, your choice of boilerplate may differ.\
The one thing to keep in mind is what the [production application structure](/application-development/deploying/application-structure) should look like when deploying the application.

We provide a boilerplate of our own that is meant to facilitate development, but does not assume anything other than your application's source code entry points (`index.html` and `index.js`). It is not as mature as the above, but for your consideration, this is why we use it:

* simple to understand and extend
* minimal infrastructure only - no non-vanilla technology is assumed
* HMR
* build step includes archive creation for my.lampix.com, along with versioned build folders (based on package.json version)
* build scripts to automatically increase patch, minor or major versions before building, alongside the "just build" script
* respects the requirements of the [production application structure](/application-development/deploying/application-structure)

The boilerplate can be found on our [GitHub](https://github.com/lampix-org/app-boilerplate).


# Step by step app

This example will showcase registering the most common watchers and how to use them, step by step.

**IMPORTANT**: At the time of this writing, this example simply showcases the steps to build an app, but it is meant to run only in the simulator, as fruits is not a neural network we currently provide.

It will be provided in the future, with oranges, lemons and limes at first, and the app adjusted to take into account the varying sizes of the physical elements. Right now, the size isn't taken into consideration.

* [What we'll build](/application-development/step-by-step/end-result)
* [Environment Setup](/application-development/step-by-step/environment-setup)
* [Styling](/application-development/step-by-step/styling)
* [HTML Structure](/application-development/step-by-step/html-structure)
* [NeuralNetworkClassifier](/application-development/step-by-step/initialize-nnc)
* [MovementBasedSegmenter](/application-development/step-by-step/initialize-mbs)
* [Final step](/application-development/step-by-step/final-step)
* [Extras](/application-development/step-by-step/extras)


# What We'll Build

As is customary, here's a preview of what the end result will look like:

![example-fruits end result](https://s3.amazonaws.com/api.lampix.co/lampixjs-v1.x.x-docs/step-by-step-app__what-we-build.jpg)


# Environment Setup

Run the following commands in a terminal:

```bash
git clone https://github.com/lampix-org/app-boilerplate.git nnc-and-mbs
cd nnc-and-mbs
npm install
```


# Styling

Copy and paste this in a `src/styles.css` file right next to `src/index.html` and `src/index.js`.

```css
body {
  color: white;
  font-family: sans-serif;
}

.area {
  border: 1px solid #FFFFFF;
  border-radius: 10px;

  transition: border-color 200ms ease;
}

.area-title {
  position: absolute;
  top: -2em;
  text-align: center;
  width: 100%;
}

.nnc {
  position: absolute;
  top: 50%;
  left: 25%;
  transform: translate(-50%, -50%);

  width: 200px;
  height: 200px;
}

.nnc-recognized-class {
  position: absolute;
  bottom: -2em;
  text-align: center;
  width: 100%;
}

.mbs {
  position: absolute;
  top: 50%;
  left: 75%;
  transform: translate(-50%, -50%);

  width: 600px;
  height: 600px;
}

.mbs-object {
  position: absolute;

  border: 3px solid;
  border-radius: 50%;
  display: flex;
  justify-content: center;
  align-items: center;
  text-align: center;

  width: 50px;
  height: 50px;

  animation: grow 1s ease forwards;
}

@keyframes grow {
  0% {
    transform: scale(0);
  }

  100% {
    transform: scale(1);
  }
}
```

Then import the file in `index.js` to let Webpack know about it and apply the styles. It will take care of linking it in `index.html` on its own when the time comes.

```javascript
// index.js
import lampix from '@lampix/core';

import './styles.css';
```


# HTML Structure

Add the snippet below in the `body` of `index.html`:

```markup
<div class="nnc area">
  <div class="area-title">Neural Network Classifier</div>
  <div class="nnc-recognized-class">No object recognized</div>
</div>
<div class="mbs area">
  <div class="area-title">Movement Based Segmenter</div>
</div>
```


# NeuralNetworkClassifier

Time to make `NeuralNetworkClassifier` watcher classify `fruits`.

Let's create an initialization function for the NNC watcher.

```javascript
const initializeNNC = () => {};
```

Retrieve the elements we'll be working with, along with the bounding rect of the element defining the watcher's contour.

```javascript
const initializeNNC = () => {
  // Get the elements we'll be working with...
  const nncElement = document.getElementsByClassName('nnc')[0];
  const nncRecognizedClassElement = document.getElementsByClassName('nnc-recognized-class')[0];

  // ...along with the bounding rect that defines the watcher size
  const nncBounds = nncElement.getBoundingClientRect();
};
```

Define the watcher data structure.

```javascript
  // ...

  const nncFruitsWatcher = {
    name: 'NeuralNetworkClassifier',
    shape: lampix.helpers.rectangle(
      nncBounds.left,
      nncBounds.top,
      nncBounds.width,
      nncBounds.height
    ),
    params: {
      neural_network_name: 'fruits'
    }
  };
```

In case you're wondering, the above `lampix.helpers.rectangle` could be replaced with:

```javascript
{
  type: 'rectangle',
  data: {
    posX: nncBounds.left,
    posY: nncBounds.top,
    width: nncBounds.width,
    height: nncBounds.height
  }
}
```

The watcher data structure above is almost complete, but it's missing one key component: **what to actually do when classification is triggered**.

```javascript
  // ...

  // All Lampix classifiers return a list of recognized objects
  // NNClassifier only recognizes one at a time, hence expecting
  // an array with one element and destructuring it
  const nncCallback = ([recognizedObject]) => {
    nncRecognizedClassElement.textContent = `Recognized: ${recognizedObject.classTag}`;

    if (Number(recognizedObject.classTag) === 1) {
      // Change border color on each new detection
      nncElement.style.borderColor = randomColor();
    } else {
      // Go back to white if object no longer there
      nncElement.style.borderColor = '#FFFFFF';
    }
  };

  const nncFruitsWatcher = {
    name: 'NeuralNetworkClassifier',
    shape: lampix.helpers.rectangle(
      nncBounds.left,
      nncBounds.top,
      nncBounds.width,
      nncBounds.height
    ),
    params: {
      neural_network_name: 'fruits'
    },
    onClassification: nncCallback
  };
```

All that's left now is to inform Lampix of its existence, by adding the following to the end of the `initializeNNC` function.

```javascript
  // ...
  lampix.watchers.add(nncFruitsWatcher);
```

Now, `initializeNNC` should look like this:

```javascript
const initializeNNC = () => {
  // Get the elements we'll be working with...
  const nncElement = document.getElementsByClassName('nnc')[0];
  const nncRecognizedClassElement = document.getElementsByClassName('nnc-recognized-class')[0];

  // ...along with the bounding rect that defines the watcher size
  const nncBounds = nncElement.getBoundingClientRect();

  // All Lampix classifiers return a list of recognized objects
  // NNClassifier only recognizes one at a time, hence expecting
  // an array with one element and destructuring it
  const nncCallback = ([recognizedObject]) => {
    nncRecognizedClassElement.textContent = `Recognized: ${recognizedObject.classTag}`;

    if (Number(recognizedObject.classTag) === 1) {
      nncElement.style.borderColor = '#FF0000';
    } else {
      // Go back to white if object no longer there
      nncElement.style.borderColor = '#FFFFFF';
    }
  };

  const nncFruitsWatcher = {
    name: 'NeuralNetworkClassifier',
    shape: lampix.helpers.rectangle(
      nncBounds.left,
      nncBounds.top,
      nncBounds.width,
      nncBounds.height
    ),
    params: {
      neural_network_name: 'fruits'
    },
    onClassification: nncCallback
  };

  lampix.watchers.add(nncFruitsWatcher);
};
```


# MovementBasedSegmenter

`MovementBasedSegmenter`'s turn to locate and classify `fruits`.

Let's create an initialization function for the MBS watcher.

```javascript
const initializeMBS = () => {};
```

Once again, retrieve the elements we'll be working with, along with the bounding rect of the element defining the watcher's contour.

```javascript
const initializeMBS = () => {
  const mbsElement = document.getElementsByClassName('mbs')[0];
  const mbsBounds = mbsElement.getBoundingClientRect();
};
```

Just like `NeuralNetworkClassifier` (and all watchers, for that matter), **MBS** also has its classification trigger on the `onClassification` callback.\
However, `MovementBasedSegmenter` has a secondary callback, triggered prior to `onClassification`, named `onLocation`. This is because MBS first determines the location of an object and then it classifies it. `onLocation` is generally used to create a loading animation for a located, not yet classified object.

For the sake of simplicity, we will focus on `onClassification` in this guide.

```javascript
const initializeMBS = () => {
  const mbsElement = document.getElementsByClassName('mbs')[0];
  const mbsBounds = mbsElement.getBoundingClientRect();

  const onClassification = (classifiedObjects) => classifiedObjects.forEach((classifiedObject) => {
    handleObjectClassified(classifiedObject, '#FFFFFF');
  });

  const onLocation = (locatedObjects) => {
    // This step fires before onClassification!
    console.log(locatedObjects);
  };

  const mbsFruitsWatcher = {
    name: 'MovementBasedSegmenter',
    shape: lampix.helpers.rectangle(
      mbsBounds.left,
      mbsBounds.top,
      mbsBounds.width,
      mbsBounds.height
    ),
    params: {
      neural_network_name: 'fruits'
    },
    onLocation,
    onClassification
  };
};
```

Let's also add the utility mentioned above to add DOM elements for each classified object (remember to import it):

```javascript
// handleObjectClassified.js
const handleObjectClassified = (obj, color) => {
  const el = document.createElement('div');
  el.classList.add('mbs-object');
  el.style.borderColor = color;
  el.style.left = `${obj.centerPoint.posX - 25}px`;
  el.style.top = `${obj.centerPoint.posY - 25}px`;
  el.textContent = obj.classTag;

  document.body.appendChild(el);
};

export default handleObjectClassified;
```

All that's left is telling Lampix about this watcher too, by adding the following to the end of the `initializeMBS` function.

```javascript
  // ...
  lampix.watchers.add(mbsFruitsWatcher);
```

Now, `initializeMBS` should look like this:

```javascript
const initializeMBS = () => {
  const mbsElement = document.getElementsByClassName('mbs')[0];
  const mbsBounds = mbsElement.getBoundingClientRect();

  const onClassification = (classifiedObjects) => classifiedObjects.forEach((classifiedObject) => {
    handleObjectClassified(classifiedObject, '#FFFFFF');
  });

  const onLocation = (locatedObjects) => {
    // This step fires before onClassification!
    console.log(locatedObjects);
  };

  const mbsFruitsWatcher = {
    name: 'MovementBasedSegmenter',
    shape: lampix.helpers.rectangle(
      mbsBounds.left,
      mbsBounds.top,
      mbsBounds.width,
      mbsBounds.height
    ),
    params: {
      neural_network_name: 'fruits'
    },
    onLocation,
    onClassification
  };

  lampix.watchers.add(mbsFruitsWatcher);
};
```


# Final Step

All that's left to do is call the initializer functions at the bottom of `index.js`:

```javascript
initializeNNC();
initializeMBS();
```

The end result should look like:

```javascript
import lampix from '@lampix/core';

import './styles.css';
import handleObjectClassified from './handleObjectClassified';

const initializeNNC = () => {
  const nncElement = document.getElementsByClassName('nnc')[0];
  const nncBounds = nncElement.getBoundingClientRect();
  const nncRecognizedClassElement = document.getElementsByClassName('nnc-recognized-class')[0];

  // All Lampix classifiers return a list of recognized objects
  // NNClassifier only recognizes one at a time, hence expecting
  // an array with one element and destructuring it
  const nncCallback = ([recognizedObject]) => {
    nncRecognizedClassElement.textContent = `Recognized: ${recognizedObject.classTag}`;

    if (Number(recognizedObject.classTag) === 1) {
      nncElement.style.borderColor = '#FF0000';
    } else {
      // Go back to white if object no longer there
      nncElement.style.borderColor = '#FFFFFF';
    }
  };

  const nncFruitsWatcher = {
    name: 'NeuralNetworkClassifier',
    shape: lampix.helpers.rectangle(
      nncBounds.left,
      nncBounds.top,
      nncBounds.width,
      nncBounds.height
    ),
    params: {
      neural_network_name: 'fruits'
    },
    onClassification: nncCallback
  };

  lampix.watchers.add(nncFruitsWatcher);
};

const initializeMBS = () => {
  const mbsElement = document.getElementsByClassName('mbs')[0];
  const mbsBounds = mbsElement.getBoundingClientRect();

  const onClassification = (classifiedObjects) => classifiedObjects.forEach((classifiedObject) => {
    handleObjectClassified(classifiedObject, '#FFFFFF');
  });

  const onLocation = (locatedObjects) => {
    // This step fires before onClassification!
    console.log(locatedObjects);
  };

  const mbsFruitsWatcher = {
    name: 'MovementBasedSegmenter',
    shape: lampix.helpers.rectangle(
      mbsBounds.left,
      mbsBounds.top,
      mbsBounds.width,
      mbsBounds.height
    ),
    params: {
      neural_network_name: 'fruits'
    },
    onLocation,
    onClassification
  };

  lampix.watchers.add(mbsFruitsWatcher);
};

initializeNNC();
initializeMBS();
```

Minus the next, optional step, this is the exact application in [the fruits example on our GitHub](https://github.com/lampix-org/example-fruits).\
If you're having trouble with this guide, see if the source code can help you out.


# Extras

## Random color

Use a utility function to generate a random color for the border of the NNC and the elements of the MBS.

```javascript
// randomColor.js
export default () => `#${Math.floor(Math.random() * 0x1000000).toString(16).padStart(6, 0)}`;
```

Import it in `index.js`.

```javascript
// ...
import randomColor from './randomColor';
```

Use it for both NNC and MBS.

```javascript
  // ...
  nncElement.style.borderColor = randomColor();
  // ...
```

```javascript
  // ...
  // Associate one class to one color
  const classColorMap = {};

  const onClassification = (classifiedObjects) => classifiedObjects.forEach((classifiedObject) => {
    let color = classColorMap[classifiedObject.classTag];

    if (!color) {
      color = randomColor();
      classColorMap[classifiedObject.classTag] = color;
    }

    handleObjectClassified(classifiedObject, color);
  });
  // ...
```

End result:

```javascript
import lampix from '@lampix/core';

import './styles.css';
import randomColor from './randomColor';
import handleObjectClassified from './handleObjectClassified';

const initializeNNC = () => {
  const nncElement = document.getElementsByClassName('nnc')[0];
  const nncBounds = nncElement.getBoundingClientRect();
  const nncRecognizedClassElement = document.getElementsByClassName('nnc-recognized-class')[0];

  // All Lampix classifiers return a list of recognized objects
  // NNClassifier only recognizes one at a time, hence expecting
  // an array with one element and destructuring it
  const nncCallback = ([recognizedObject]) => {
    nncRecognizedClassElement.textContent = `Recognized: ${recognizedObject.classTag}`;

    if (Number(recognizedObject.classTag) === 1) {
      // Change border color on each new detection
      nncElement.style.borderColor = randomColor();
    } else {
      // Go back to white if object no longer there
      nncElement.style.borderColor = '#FFFFFF';
    }
  };

  const nncFruitsWatcher = {
    name: 'NeuralNetworkClassifier',
    shape: lampix.helpers.rectangle(
      nncBounds.left,
      nncBounds.top,
      nncBounds.width,
      nncBounds.height
    ),
    params: {
      neural_network_name: 'fruits'
    },
    onClassification: nncCallback
  };

  lampix.watchers.add(nncFruitsWatcher);
};

const initializeMBS = () => {
  const mbsElement = document.getElementsByClassName('mbs')[0];
  const mbsBounds = mbsElement.getBoundingClientRect();

  // Associate one class to one color
  const classColorMap = {};

  const onClassification = (classifiedObjects) => classifiedObjects.forEach((classifiedObject) => {
    let color = classColorMap[classifiedObject.classTag];

    if (!color) {
      color = randomColor();
      classColorMap[classifiedObject.classTag] = color;
    }

    handleObjectClassified(classifiedObject, color);
  });

  const onLocation = (locatedObjects) => {
    // This step fires before onClassification!
    console.log(locatedObjects);
  };

  const mbsFruitsWatcher = {
    name: 'MovementBasedSegmenter',
    shape: lampix.helpers.rectangle(
      mbsBounds.left,
      mbsBounds.top,
      mbsBounds.width,
      mbsBounds.height
    ),
    params: {
      neural_network_name: 'fruits'
    },
    onLocation,
    onClassification
  };

  lampix.watchers.add(mbsFruitsWatcher);
};

initializeNNC();
initializeMBS();
```


# LampixJS

* [API Reference](/application-development/lampixjs/api)
  * [Watcher](/application-development/lampixjs/api/watcher)
  * [RegisteredWatcher](/application-development/lampixjs/api/registered-watcher)
  * [.watchers.add](/application-development/lampixjs/api/watchers.add)
  * [.watchers.remove](/application-development/lampixjs/api/watchers.remove)
  * [.watchers.pauseAll](/application-development/lampixjs/api/watchers.pauseall)
  * [.watchers.resumeAll](/application-development/lampixjs/api/watchers.resumeall)
  * [.presets.button](/application-development/lampixjs/api/presets.button)
  * [.helpers.rectangle](/application-development/lampixjs/api/helpers.rectangle)
  * [getLampixInfo](/application-development/lampixjs/api/getlampixinfo)
  * [switchToApp](/application-development/lampixjs/api/switchtoapp)
  * [exit](/application-development/lampixjs/api/exit)
  * [getApps](/application-development/lampixjs/api/getapps)
  * [getAppConfig](/application-development/lampixjs/api/getappconfig)
  * [getAppMetadata](/application-development/lampixjs/api/getappmetadata)
  * [writeJsonToFile](/application-development/lampixjs/api/writejsontofile)
  * [readJsonFromFile](/application-development/lampixjs/api/readjsonfromfile)
  * [transformRectCoords](/application-development/lampixjs/api/transformrectcoords)
  * [constants](/application-development/lampixjs/api/constants)
* [Examples](/application-development/lampixjs/examples)
  * [NeuralNetworkClassifier: buttons](/application-development/lampixjs/examples/buttons)
  * [MovementBasedSegmenter](/application-development/lampixjs/examples/movement-based-segmenter)
  * [Counter app](/application-development/lampixjs/examples/counter-app)
* [Migrating from v0.x.x to v1.0.0-beta.x](/application-development/lampixjs/migration-guide)
* [Ecosystem](/application-development/lampixjs/ecosystem)


# API Reference

## Objects

* [Watcher](/application-development/lampixjs/api/watcher)
* [RegisteredWatcher](/application-development/lampixjs/api/registered-watcher)

## Methods

* [.watchers.add](/application-development/lampixjs/api/watchers.add)
* [.watchers.remove](/application-development/lampixjs/api/watchers.remove)
* [.watchers.pauseAll](/application-development/lampixjs/api/watchers.pauseall)
* [.watchers.resumeAll](/application-development/lampixjs/api/watchers.resumeall)
* [.presets.button](/application-development/lampixjs/api/presets.button)
* [.helpers.rectangle](/application-development/lampixjs/api/helpers.rectangle)
* [getLampixInfo](/application-development/lampixjs/api/getlampixinfo)
* [switchToApp](/application-development/lampixjs/api/switchtoapp)
* [exit](/application-development/lampixjs/api/exit)
* [getApps](/application-development/lampixjs/api/getapps)
* [getAppConfig](/application-development/lampixjs/api/getappconfig)
* [getAppMetadata](/application-development/lampixjs/api/getappmetadata)
* [writeJsonToFile](/application-development/lampixjs/api/writejsontofile)
* [readJsonFromFile](/application-development/lampixjs/api/readjsonfromfile)
* [transformRectCoords](/application-development/lampixjs/api/transformrectcoords)
* [constants](/application-development/lampixjs/api/constants)


# Watcher

Watcher objects are plain object descriptors providing Lampix with the required information to start watching areas based on the behaviors described.

## Properties

* `name` (*string*): Literal string describing the Python class to instantiate and define the watcher's behavior (e.g `'NeuralNetworkClassifier'`, `'MovementBasedSegmenter'` etc.).
* `shape` (*Object*): Plain object with two properties, `type` and `shape` that defines the surface area for the watcher.
  * `type` (*string*): Defines shape type, and controls what the expected structure of `shape.data` will be. Accepted values:
    * `'rectangle'`
    * `'polygon'`
  * `data` (*Object*): Actual descriptor object for the contour of the watcher.
    * If `shape.type` is `'rectangle'`, the expected data structure is:
      * `posX` (*number*): top left X coordinate of rectangle
      * `posY` (*number*): top left Y coordinate of rectangle
      * `width` (*number*)
      * `height` (*number*)
    * If `shape.type` is `'polygon'`, a list of points is expected, with each point having the following data structure:
      * `x` (*number*): X coordinate of point
      * `y` (*number*): Y coordinate of point
* `onClassification` (*Function*): Callback used by Lampix to send information about recognized objects as a list with with recognized objects of the following structure:
  * `classTag` (*string*): Recognized class of the described object.
  * `objectId?` (*number, Optional*): ID representing the object. Useful when determining whether Lampix considers an object as new.
  * `outline?` (*Array, Optional*): Object with a property of `points` that describes the contour of the object as a polygon:
  * `metadata?` (*string*, Optional): Watcher specific information
* `onLocation` (*Function*): Callback used only by watchers that locate first, then classify. Called with a list of located objects that with a similar structure to the one above, except `classTag` is not mentioned since it has not been determined at the time of this call.
* `params?` (*Object*): Plain object with arbitrary props that can differ from watcher to watcher. See [standard watchers](/application-development/standard-watchers)

## Notes

The only required property is `classTag`. Certain watchers that do not support more than one object at a time (e.g `NeuralNetworkClassifier`), will always provide a list with one recognized object that only has the `classTag` property. This is by design, to ensure consistency in the data format returned by the standard watchers.

## Example

```javascript
{
  name: 'DepthClassifier',
  shape: {
    type: "rectangle",
    data: {
      posX: 0,
      posY: 0,
      width: window.innerWidth,
      height: window.innerHeight
    }
  },
  params: {
    frames_until_stable: 5
  },
  onClassification: (objects) => draw(objects);
}
```

## Tips

In the example above, you can use `shape: lampix.helpers.rectangle(0, 0, window.innerWidth, window.innerHeight)` to achieve the same result.


# RegisteredWatcher

Registered watcher objects are plain objects with a few convenience methods and properties. These objects can only be obtained via the [`.watchers.add`](/application-development/lampixjs/api/watchers.add) method (for the time being), and **all watcher management is done through the interface they provide**. If watcher management (e.g pausing, resuming, removing, updating) is required by an application, they should be saved in the app's state.

## Properties

* `source` (*Object*): A replica of the object provided to `.watchers.add`.
* `state` (*Object*): An object representing the current state of the registered watcher.
  * `active` (*boolean*): Represents whether computer vision processes are active for the watcher in question. Changed by `pause` and `resume` methods.
* `onClassification` (*Function*): Configurable handler for the classification event. See [Watcher](/application-development/lampixjs/api/watcher) for details.
* `onLocation` (*Function*, Optional): Configurable handler for the location event. See [Watcher](/application-development/lampixjs/api/watcher) for details.
* `channel` (*Object*): Channel to use to send data to the watcher past the point of initialization

## Methods

Note that all of these methods return a promise with no resolve value.

### [`pause()`](/application-development/lampixjs/api/registered-watcher#pause) <a href="#pause" id="pause"></a>

Pauses computer vision activity (classification, location) for the registered watcher.

### [`resume()`](/application-development/lampixjs/api/registered-watcher#resume) <a href="#resume" id="resume"></a>

Resumes computer vision activity (classification, location) for the registered watcher.

### [`remove()`](/application-development/lampixjs/api/registered-watcher#remove) <a href="#remove" id="remove"></a>

Removes the registered watcher.

### [`channel.send(data)`](/application-development/lampixjs/api/registered-watcher#watcher-channel) <a href="#watcher-channel" id="watcher-channel"></a>

Sends data to be acted upon by all listeners registered in the watcher. Mainly for use with custom watchers. Can be anything valid according to the [`JSON.stringify`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify) method

#### Notes

Adding and removing watchers can have an impact on performance. If you intend to reuse the same watcher, consider using the [`pause`](/application-development/lampixjs/api/registered-watcher#pause) and [`resume`](/application-development/lampixjs/api/registered-watcher#resume) methods instead.

### [`updateShape(shape)`](/application-development/lampixjs/api/registered-watcher#updateShape) <a href="#updateshape" id="updateshape"></a>

Replaces the shape of a registered watcher with the new one provided.


# .watchers.add

Registers one or more areas to watch with Lampix.

## Arguments

1. `...watchers` ([`Watcher[]`](/application-development/lampixjs/api/watcher)) Comma separated watcher objects or one level deep arrays of watcher objects.

## Returns

([`Promise<RegisteredWatcher[]>`](/application-development/lampixjs/api/registered-watcher)): A promise that fulfills with a list of [`RegisteredWatcher`](/application-development/lampixjs/api/registered-watcher) objects, equal in length to the number of watcher objects provided to the function

## Example

```javascript
import lampix from '@lampix/core';

const draw = (recognizedObject) => {
  // Drawing amazing effects
};

const watcher = {
  name: 'DepthClassifier',
  shape: lampix.helpers.rectangle(0, 0, window.innerWidth, window.innerHeight),
  onClassification: (recognizedObjects) => {
    recognizedObjects.forEach(draw);
  }
};

lampix.watchers.add(watcher)
  .then((registeredWatchers) => {
    console.log(registeredWatchers.length); // 1
  });
```

## Notes

If the method receives arrays, it will concatenate them into a single array that will be sent to Lampix. The promise will be resolved with a single array of watchers, in the same order that they were added. For instance:

```javascript
import lampix from '@lampix/core';

const draw = (recognizedObject) => {
  // Drawing amazing effects
};

const watcher = {
  name: 'DepthClassifier',
  shape: lampix.helpers.rectangle(0, 0, window.innerWidth, window.innerHeight),
  onClassification: (recognizedObjects) => {
    recognizedObjects.forEach(draw);
  }
};

const otherWatchers = {
  {
    // ...
  },
  {
    // ...
  }
}

lampix.watchers.add(watcher, otherWatchers)
  .then((registeredWatchers) => {
    console.log(registeredWatchers.length); // 3
    console.log(registeredWatchers); // registered watchers corresponding to watcher, otherWatchers[0] and otherWatchers[1], in this order
  });
```


# .watchers.remove

Removes one or more registered watchers from Lampix.

## Arguments

1. `...registeredWatchers` ([`RegisteredWatcher[]`](/application-development/lampixjs/api/registered-watcher)) Comma separated RegisteredWatcher objects

## Returns

(`Promise<void>`): A promise that fulfills when all of the registered watchers have been removed from Lampix.

## Example

```javascript
import lampix from '@lampix/core';

const draw = (recognizedObjects) => {
  // Drawing amazing effects
};

const watcher1 = {
  name: 'DepthClassifier',
  shape: lampix.helpers.rectangle(0, 0, window.innerWidth / 2, window.innerHeight),
  onClassification: draw
};

const watcher2 = {
  name: 'DepthClassifier',
  shape: lampix.helpers.rectangle(window.innerWidth / 2, 0, window.innerWidth / 2, window.innerHeight),
  onClassification: draw
};

lampix.watchers.add(watcher1, watcher2)
  .then((registeredWatchers) => {
    // Remove them right away!
    // The removal expression below is equivalent to 
    // 1. lampix.watchers.remove.apply(null, registeredWatchers);
    // 2. lampix.watchers.remove(registeredWatchers[0], registeredWatchers[1]);
    return lampix.watchers.remove(...registeredWatchers);
  })
  .then(() => {
    console.log('Registered watchers removed');
  });
```


# .watchers.pauseAll

Pauses all the currently registered watchers.\
**NOTE**: If a watcher is currently pending registration with Lampix, it will **NOT be paused**, as this methods concerns all of the watchers that have been registered successfully.

## Returns

(`Promise<void>`): A promise that fulfills when all of the registered watchers have been paused. If a registered watcher is already paused, it will simply resolve automatically, bypassing Lampix.

## Example

```javascript
import lampix from '@lampix/core';

const state = {
  registeredWatchers: []
};

const draw = (recognizedObjects) => {
  // Drawing amazing effects
};

const watcher1 = {
  name: 'DepthClassifier',
  shape: lampix.helpers.rectangle(0, 0, window.innerWidth / 2, window.innerHeight),
  onClassification: draw
};

const watcher2 = {
  name: 'DepthClassifier',
  shape: lampix.helpers.rectangle(window.innerWidth / 2, 0, window.innerWidth / 2, window.innerHeight),
  onClassification: draw
};

lampix.watchers.add(watcher1, watcher2)
  .then((registeredWatchers) => {
    state.registeredWatchers = registeredWatchers;

    // Remove them right away!
    // The removal expression below is equivalent to 
    return lampix.watchers.pauseAll();
  })
  .then(() => {
    // watcher1 and watcher2 are now paused
    console.log(registeredWatchers[0].state.active); // false
    console.log(registeredWatchers[1].state.active); // false
  });
```

## Notes

You can pause a single registered watcher by using its [`.pause()`](/application-development/lampixjs/api/registered-watcher#pause) method as well.


# .watchers.resumeAll

Resumes all the currently registered watchers.

## Returns

(`Promise<void>`): A promise that fulfills when all of the registered watchers have been resumed. If a registered watcher is not paused, it will simply resolve automatically, bypassing Lampix.

## Example

```javascript
import lampix from '@lampix/core';

const state = {
  registeredWatchers: []
};

const draw = (recognizedObjects) => {
  // Drawing amazing effects
};

const watcher1 = {
  name: 'DepthClassifier',
  shape: lampix.helpers.rectangle(0, 0, window.innerWidth / 2, window.innerHeight),
  onClassification: draw
};

const watcher2 = {
  name: 'DepthClassifier',
  shape: lampix.helpers.rectangle(window.innerWidth / 2, 0, window.innerWidth / 2, window.innerHeight),
  onClassification: draw
};

lampix.watchers.add(watcher1, watcher2)
  .then((registeredWatchers) => {
    state.registeredWatchers = registeredWatchers;

    // Remove them right away!
    // The removal expression below is equivalent to 
    return lampix.watchers.pauseAll();
  })
  .then(() => {
    // watcher1 and watcher2 are now paused
    console.log(registeredWatchers[0].state.active); // false
    console.log(registeredWatchers[1].state.active); // false

    return lampix.watchers.resumeAll();
  })
  .then(() => {
    // watcher1 and watcher2 are now resumed
    console.log(registeredWatchers[0].state.active); // true
    console.log(registeredWatchers[1].state.active); // true
  });
```

## Notes

You can resume a single registered watcher by using its [`.resume()`](/application-development/lampixjs/api/registered-watcher#resume) method as well.


# .presets.button

Create a watcher object prepared to be used as a button using the standard neural network and watcher name for finger recognition.

## Returns

([`Watcher`](/application-development/lampixjs/api/watcher)): Plain object descriptor for a watcher ready to be registered.

## Example

**NOTE** that all of the ways to create a button specified below are equivalent.

```javascript
import lampix from '@lampix/core';

const callback = ([recognizedObject]) => {
  if (Number(recognizedObject.classTag) === 1) {
    console.log('yay!');
  } else {
    console.log('nay!');
  }
};

// Showcasing how to do it the hard way first
const doingThingsTheHardWay = {
  name: 'NeuralNetworkClassifier',
  shape: {
    type: 'rectangle',
    data: {
      posX: 50,
      posY: 50,
      width: 50
      height: 50
    }
  },
  onClassification: callback,
  params: {
    neural_network_name: 'fingers'
  }
};

// And the easy way after
const doingThingsTheEasyWay = lampix.presets.button(50, 50, callback);

// Specifying width and height as well
const anotherEasyWatcher = lampix.presets.button(50, 50, callback, {
  width: 50,
  height: 50
});
```


# .helpers.rectangle

Create a shape object for a watcher descriptor.

## Returns

```javascript
{
  type: 'rectangle',
  data: {
    posX: number,
    posY: number,
    width: number,
    height: number
  }
}
```

## Example

**NOTE** that all of the ways to create a button specified below are equivalent.

```javascript
import lampix from '@lampix/core';

const callback = ([recognizedObject]) => {
  if (Number(recognizedObject.classTag) === 1) {
    console.log('yay!');
  } else {
    console.log('nay!');
  }
};

const watcher = {
  name: 'NeuralNetworkClassifier',
  shape: lampix.helpers.rectangle(50, 50, 50, 50),
  onClassification: callback,
  params: {
    neural_network_name: 'fingers'
  }
};
```


# getLampixInfo

Retrieve environment (Lampix or simulator) information.

## Returns

(`Promise<LampixInfo>`): A promise that fulfills with a plain descriptor object with the following properties:

* `id` (*string*): Unique ID for Lampix or simulation
* `version` (*string*): Current version of Lampix or the simulator
* `isSimulator` (*boolean*): Whether the current environment is a simulation or Lampix

## Example

```javascript
import lampix from '@lampix/core';

lampix.getLampixInfo()
  .then(console.log); // example { id: <unique-id>, version: '1.2.3', isSimulator: false }
```


# switchToApp

Change from one application to another.

## Arguments

1. `appName` (*string*): Application name equivalent to the value specified in the application's `package.json` file.
2. `queryParameters` (*Object, Optional*): Plain object with keys used query parameters with their respective values. **Note that keys will be converted from camelCase to kebab-case**.

## Returns

(`Promise<void>`): A promise that fulfills without arguments.

## Example

```javascript
import lampix from '@lampix/core';

lampix.switchToApp('trivia');

// or

lampix.switchToApp('trivia', {
  switchBackTo: 'survey',
  specialInformation: 42
});

// Query parameters can be easily accessed as follows:
// (also note the camelCase to kebab-case transformation)
// const queryParams = new URLSearchParams(window.location.search);
// queryParams.get('switch-back-to'); // survey
// queryParams.get('special-information') // '42'
```

## Notes

`switchBackTo` is a special query parameter used in the [`exit()`](https://github.com/lampix-org/lampixjs-core/tree/aae985faf1cdc0c2159f49817193a66853c0eb5d/docs/app-dev/lampixjs/api/exit.md) method to determine whether to switch to the default app (currently `app-switcher`) or the app specified as the value for this parameter.


# exit

Switch back to the default app or the value of the `switch-back-to` query param specified via [`switchBackTo()`](https://github.com/lampix-org/lampixjs-core/tree/6b15a76a59ba8ba1b337b8aa71d4a72ca208b4c4/docs/app-dev/lampixjs/api/switchBackTo.md).

(`Promise<void>`): A promise that fulfills without arguments.

## Example

```javascript
import lampix from '@lampix/core';

lampix.exit();
```


# getApps

Retrieve the available apps to switch to. Currently only used by the `app-switcher`.

## Returns

(`Promise<AppInfo[]>`): A promise that fulfills with a list of plain objects describing the available apps. `AppInfo` objects have the following properties:

* `name` (*string*): App name.
* `package_data` (*Object*): Information contained in `package.json` for each app.

## Example

```javascript
import lampix from '@lampix/core';

lampix.getApps()
  .then((apps) => {
    console.log(apps[0]) // { name: some-name, package_data: {} }
  });
```


# getAppConfig

Retrieve the contents of the `config.json` file.

## Returns

(`Promise<object>`): A promise that fulfills with a plain object.

## Example

```javascript
import lampix from '@lampix/core';

const initialize = (config) => {
  // do something with the data in config.json
};

lampix.getAppConfig()
  .then((config) => {
    initialize(config);
  });
```

## Notes

See [`config.json`](/application-development/deploying/application-structure#config-and-schema) of the [production application structure](/application-development/deploying/application-structure) for details on what `config.json` is used for.


# getAppMetadata

Retrieve the contents of the `package.json` file.

## Returns

(`Promise<object>`): A promise that fulfills with a plain object.

## Example

```javascript
import lampix from '@lampix/core';

lampix.getAppMetadata()
  .then((pkg) => {
    console.log(pkg.name); // logs the name property from package.json
  });
```


# writeJsonToFile

Write JSON data to a file to retrieve later via [`readJsonFromFile`](/application-development/lampixjs/api/readjsonfromfile).

## Returns

(`Promise<void>`): A promise that fulfills without arguments when writing has finished.

## Example

```javascript
import lampix from '@lampix/core';

const data = {
  answerToLife: 42
};

lampix.writeJsonToFile('answers.json', data)
  .then(() => console.log('Successfully saved all answers.'));
```

## Notes

[`localStorage`](https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage) API is also enabled. You can use it instead of this method. `localStorage`, by design, is a blocking I/O operation (synchronous). Use `writeJsonToFile` and [readJsonFromFile](/application-development/lampixjs/api/readjsonfromfile) if you want to use non-blocking (asynchronous) I/O operations.

`writeJsonToFile` (like `localStorage`), is application specific. This means one application cannot retrieve the saved data of another application.


# readJsonFromFile

Read JSON data from a file written with [`writeJsonToFile`](/application-development/lampixjs/api/writejsontofile).

## Returns

(`Promise<object>`): A promise that fulfills with either `null` (in case the file does not contain valid JSON, or if the file does not exist) or an object with the contents of the file.

## Example

```javascript
import lampix from '@lampix/core';

lampix.readJsonFromFile('answers.json')
  .then(console.log); // { answerToLife: 42 }
```

## Notes

[`localStorage`](https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage) API is also enabled. You can use it instead of this method. `localStorage`, by design, is a blocking I/O operation (synchronous). Use [`writeJsonToFile`](/application-development/lampixjs/api/writejsontofile) and `readJsonFromFile` if you want to use non-blocking (asynchronous) I/O operations.

`readJsonFromFile` (like `localStorage`), is application specific. This means one application cannot retrieve the saved data of another application.


# transformRectCoords

Convert coordinates from a rectangle descriptor using camera coordinates to projector coordinates (or vice versa).

## Returns

(`Promise<RectCoords[]>`): A promise that fulfills with a list of rectangle descriptors with converted coordinates.

## Example

```javascript
import lampix from '@lampix/core';

lampix.transformRectCoords({
  posX: 100,
  posY: 200,
  width: 30,
  height: 100,
  camera: true
}).then((transformCoordinates) => {
  console.log(transformCoordinates[0]); // fictitious values: { posX: 200, posY: 400, width: 60, height: 200 }
});
```

## Notes

**It's important to understand** the difference between Lampix and the simulator when using this function:

* Lampix will return real coordinates as seen in either the camera or the projector.
* The simulator will return values that depend on the `scaleFactor` property in its config, which defaults to `1`, meaning it will return the same values if the `scaleFactor` was not changed.


# constants

## Properties

* `APP_SWITCHER_NAME`: useful when doing explicit switching to the app switcher via [`switchToApp`](/application-development/lampixjs/api/switchtoapp)


# Examples

* [NeuralNetworkClassifier: buttons](/application-development/lampixjs/examples/buttons)
* [MovementBasedSegmenter](/application-development/lampixjs/examples/movement-based-segmenter)
* [Counter app](/application-development/lampixjs/examples/counter-app)


# NeuralNetworkClassifier: Buttons

## The easiest way

`npm install @lampix/core @lampix/dom`

```javascript
import lampixDOM from '@lampix/dom';

// Behind the scenes, this will draw a button with a scaling animation
const x = window.innerWidth / 2;
const y = window.innerHeight / 2;
const callback = () => {
  console.log('Button activated!');
};

// Minimum necessary
lampixDOM.buttons.generate(x - 100, y, callback)
  .then((firstButton) => console.log('Button ready to be used'));

// A little configuration goes a long way
const options = {
  label: 'Generic button',
  labelPosition: 'top',
  scaleFactor: 1.2, // base animation is a simple scale animation to provide action feedback
  animationDuration: 350 // enables a circle-filling style loader and syncs the scaling animation to this value as well
};

lampixDOM.buttons.generate(x + 100, y, callback, options)
  .then((secondButton) => console.log('Another button ready to be used'));
```

## Hooking up your own button using the button preset

`npm install @lampix/core`

```javascript
import lampix from '@lampix/core';

// Assuming there is an element with an ID of 'superb-btn`
const btn = document.getElementById('superb-btn');
const btnBounds = btn.getBoundingClientRect();
const x = btnBounds.left;
const y = btnBounds.top;
const callback = () => {
  console.log('Button activated!');
};

// Use the button preset  
// This automatically takes care of creating the proper watcher data structure for you  
// It also specifies the correct watcher to load and the correct neural network to use with it
const buttonWatcher = lampix.presets.button(x, y, callback);

// Remember: .watchers.add always returns an array of registered watchers
// of the same length as the number of arguments passed to it
lampix.watchers.add(buttonWatcher)
  .then((listOfButtons) => {
    console.log('Button ready to be used');
    console.log(listOfButtons[0]);
  });
```

## Hooking up your own button creating the watcher data structure yourself

```javascript
import lampix from '@lampix/core';

// Assuming there is an element with an ID of 'superb-btn`
const btn = document.getElementById('superb-btn');
const btnBounds = btn.getBoundingClientRect();
const x = btnBounds.left;
const y = btnBounds.top;
const callback = () => {
  console.log('Button activated!');
};

const buttonWatcher = {
  name: 'NeuralNetworkClassifier',
  shape: {
    type: 'rectangle',
    data: {
      posX: x,
      posY: y,
      width: 50,
      height: 50
    }
  },
  onClassification: callback,
  params: {
    neural_network_name: 'fingers'
  }
};

// Remember: .watchers.add always returns an array of registered watchers
// of the same length as the number of arguments passed to it
lampix.watchers.add(buttonWatcher)
  .then((listOfButtons) => {
    console.log('Button ready to be used');
    console.log(listOfButtons[0]);
  });
```


# MovementBasedSegmenter

`MovementBasedSegmenter` uses a convolutional neural network to classify objects. `MovementBasedSegmenter` can detect (i.e locate and classify) multiple objects at a time in the specified watcher shape.

Example usage:

```javascript
import lampix from '@lampix/core';

const watcher = {
  name: 'MovementBasedSegmenter',
  shape: {
    type: 'rectangle',
    data: {
      posX: 0,
      posY: 0,
      width: window.innerWidth,
      height: window.innerHeight
    }
  }
  params: {
    neural_network_name: 'fruits',
    filter_circle: { min_radius: 50, max_radius: 150, min_area_ratio: 0.7 },
    filter_area: { min_ratio: 3000, max_ratio: 70000 },
    filter_thresh: 55
  }
}

// Remember: .watchers.add always returns an array of registered watchers
// of the same length as the number of arguments passed to it
lampix.watchers.add(watcher)
  .then((listOfWatchers) => console.log(listOfWatchers[0]));
```

See [standard watchers](/application-development/standard-watchers) for more information MBS.


# Counter App

The code below is available in the [minimal sample](https://github.com/lampix-org/minimal-sample) on our GitHub, that is based on our [boilerplate](https://github.com/lampix-org/lampixjs-core/tree/639edea90f491f5037788d0e41d191d094f22c94/docs/app-dev/lampixjs/boilerplate.md).

```javascript
import lampix from '@lampix/core';
import lampixDOM from '@lampix/dom';

import './styles.css';

let counter = 0;

const counterElement = document.getElementsByClassName('counter')[0];
counterElement.textContent = 'Loading...';

const increaseCount = () => {
  counter++;
};

const updateCounterElement = () => {
  counterElement.textContent = counter;
};

const initialize = async () => {
  const counterButtonOptions = {
    label: 'Increase count',
    labelPosition: 'top',
    scaleFactor: 1.2,
    animationDuration: 250
  };

  const closeButtonOptions = {
    label: 'Close App',
    labelPosition: 'top',
    scaleFactor: 1.2,
    animationDuration: 500
  };

  const callback = () => {
    increaseCount();
    updateCounterElement();
  };

  const counterButtonPromise = lampixDOM.buttons.generate(
    window.innerWidth / 2,
    window.innerHeight - 120,
    callback,
    counterButtonOptions
  );

  const closeAppButtonPromise = lampixDOM.buttons.generate(
    100,
    100,
    lampix.exit,
    closeButtonOptions
  );

  await counterButtonPromise;
  await closeAppButtonPromise;

  updateCounterElement();
};

initialize();
```


# Migrating from v0.x.x to v1.0.0-beta.x

## 0.x.x to 1.x.x

* [Watcher data structure](/application-development/lampixjs/migration-guide#watcher-data-structure)
* [Watcher names for commonly used classifier strings](/application-development/lampixjs/migration-guide#watcher-names-for-commonly-used-classifier-strings)
* [Watcher add and remove](/application-development/lampixjs/migration-guide#watcher-add-and-remove)
* [Promise based API](/application-development/lampixjs/migration-guide#promise-based-api)
* [Registered watcher object](/application-development/lampixjs/migration-guide#registered-watcher-object)
* [Watcher index no longer necessary](/application-development/lampixjs/migration-guide#watcher-index-no-longer-necessary)

## Watcher data structure

### v0.x.x

```javascript
{
  posX: number,
  posY: number,
  width: number,
  height: number,
  classifier: string // 'cls_loc_fin_all_small`, or 'segm_nes|thresh:0.5,circle:0.7,area:0.4'
}
```

The structure above represents the data required to create a watcher in **v0.x.x**.\
Watcher shape can only be a rectangle. Passing extra information to Lampix can only be done through the `.classifier` prop via the parameters sent in an inline string.

### v1.x.x

```javascript
{
  shape: {
    type: 'rectangle' | 'polygon',
    // rectangle
    data: { posX: number, posY: number, width: number, height: number }
    // or polygon
    data: [{ x: number, y: number }, { x: number, y: number }, { x: number, y: number }]
  },
  // NeuralNetworkClassifier, DepthClassifier, MovementBasedSegmenter etc.
  name: string,
  // params are optional
  // they will differ based on the name property
  // neural_network_name is a prop used with NeuralNetworkClassifier
  params: {},
  // Action to be triggered when something is classified inside the watcher
  onClassification: Function,
  // Optional
  // Called before onClassification with contour information for the located objects
  onLocation: Function
}
```

* `shape` - describes the outline of the watcher. You may use `lampix.helpers.rectangle(x, y, w, h)` or `lampix.helpers.polygon([...])` to create the shape object.
* `name` - specifies the logic to run for the watcher ([examples](/application-development/lampixjs/migration-guide#watcher-names))
* `params` - provides further information that may be required based on the `name` prop
* `onClassification` - function triggered by Lampix when something is classified inside the watcher
* `onLocation` - optional function triggered by Lampix watchers before `onClassification`

## Watcher names for commonly used classifier strings

### cls\_loc\_fin\_all\_small

* uses a neural network and doesn't provide location information => `name: 'NeuralNetworkClassifier'`
* need to specify neural network name => `params: { neural_network_name: 'fingers' }`

```javascript
{
  name: 'NeuralNetworkClassifier',
  params: {
    neural_network_name: 'fingers'
  },
  ...
}
```

You can also use the `presets.button` as seen below:

```javascript
import lampix from '@lampix/core';

function someCallback() {
  console.log('I will be called when a finger is recognized at x: 50, y: 50');
}

const w = lampix.presets.button(50, 50, someCallback);
lampix.watchers.add(w).then(([rw]) => console.log(w));
```

### segm\_\*

This applies to former *position classifiers* (currently referred to as *segmenters*) whose string started with `segm_`, such as `segm_cls_loc_nes`, `segm_cls_loc_cars`, `segm_cls_loc_bar` etc.

* uses a neural network => `name: 'MovementBasedSegmenter'`
* need to specify neural network name => `params: { neural_network_name: '*' }`, where \* represents the strings after `segm_cls_loc`, `segm_cls_` or `segm_`

## Watcher add and remove

### v0.x.x

```javascript
import lampix from '@lampix/core';

const watchers = [
  { 
    posX: 100,
    posY: 100,
    width: 50,
    height: 50,
    classifier: 'cls_loc_fin_all_small'
  },
  {
    posX: 200,
    posY: 100,
    width: 50,
    height: 50,
    classifier: 'cls_loc_fin_all_small'
  }
];

lampix.registerSimpleClassifier(watchers, (index, recognizedClass) => {
  console.log(`Watcher ${index}, class: ${recognizedClass}`);
});
```

### v1.x.x

```javascript
import lampix from '@lampix/core';

const w1 = {
  name: 'NeuralNetworkClassifier',
  shape: lampix.helpers.rectangle(100, 100, 50, 50),
  params: {
    neural_network_name: 'fingers'
  },
  onClassification: (recognizedObjects) => console.log(`Watcher 1, class: ${recognizedObjects[0].classTag}`)
};

const w2 = {
  name: 'MovementBasedSegmenter',
  shape: lampix.helpers.rectangle(200, 200, 300, 300),
  params: {
    neural_network_name: 'fruits'
  },
  onClassification: (detectedObjects) => detectedObjects.forEach((do) => console.log(do.classTag, do.outline)),
  onLocation: (locatedObjects) => locatedObjects.forEach((lo) => console.log(lo.outline))
};

lampix.watchers.add(w1, w2);
```

## Promise based API

**v0.x.x** doesn't notify when an action has completed on the device.\
**v1.x.x** fixes this issue via Promises.

```javascript
lampix.watchers.add(w1, w2).then(() => console.log('Watchers ready to be used'));
lampix.watchers.remove(registeredWatcher1, registeredWatcher2).then(() => console.log('Watchers removed'));
lampix.getLampixInfo().then((data) => console.log('Lampix info: ', data));

// ...
```

## Registered watcher object

`lampix.watchers.add(w1, w2, ..., wN)` returns a Promise that resolves with *N* objects through which the newly added watchers can be managed.

The registered watcher object provides convenience features such as `.remove()`, `.pause()` and `.resume()`.\
The information used to create the registered watcher can be found in the `.source` prop of the registered watcher object.

See the API reference for more information.

```javascript
lampix.watchers.add(w1, w2).then((rw1, rw2) => {
  // Now that we can safely use these watchers, let's pause them
  rw1.pause(); // pause indefinitely
  rw2.pause(5000); // pause for 5 seconds only
});
```

## Watcher index no longer necessary

**v0.x.x** provides the index of the watcher where movement is detected along with other relevant information (classes, outlines, metadata), but it is up to the user to always remember what watcher corresponds to a particular index in order to perform custom actions per watcher.

**v1.x.x** fixes this issue by allowing the user to add an `.onClassification` property on watchers either on the source data for the watcher or on the registered watcher itself.

```javascript
// ===== v1.x.x =====

const w1 = {
  ...,
  onClassification: () => console.log('Action specific to w1');
}

const w2 = {
  ...
  // This one gets no onClassification prop
};

lampix.watchers.add(w1, w2).then((rw1, rw2) => {
  // Setting onClassification handler for the second watcher at a later time
  // Same works for onLocation
  rw2.onClassification = () => console.log('Action specific to w2');
});
```


# Ecosystem

* [@lampix/dom](https://www.npmjs.com/package/@lampix/dom)
  * need a DOM button on the fly? Use the `.buttons.generate(x, y, callback)` functionality
* [@lampix/screensaver](https://www.npmjs.com/package/@lampix/screensaver)
  * use `.initialize(numberOfSeconds)`, where `numberOfSeconds` is an optional parameter (defaults to 90) that specifies how long to wait after the last action until the **Lampix themed** screensaver comes up


# Deploying

## Local Deploy

* [Preparing the app](/application-development/deploying/application-structure)
* [Uploading to Lampix](/application-development/deploying/local-upload)


# Application Structure (production)

```
- index.html (required)
- package.json (required)
- config.json (optional)
- schema.json (optional)
- <rest of the app files>
```

## index.html

This is the entry point of an application, and must exist in order for an app to start.

## package.json

Each application must have a package.json metadata file that describes the app. The fields that are most relevant to Lampix, my.lampix.com and / or the App Switcher are:

* **name**, *required* (e.g "app-switcher", "trivia", "restaurant", "game-changing-app-nine-thousand" etc.)
  * the name must not start with a dot or an underscore
  * new packages must not have uppercase letters in the name
  * only letters found in the English alphabet are allowed
  * no special characters other than "-" can be used
* **version**, , *required* following [semver rules](https://semver.org/)
* **description**, *required* a short introduction for the app that will be displayed in the App Switcher
* **icon**, relative path to an icon to use as the app logo in the App Switcher
* **author**, *required* an object with the required *name* and *email* fields, and an optional *url* field to point to the author's site
* **displayName**, used to set a different name than in the **name** to be used in the App Switcher (can be any valid string)
* **lampixConfig**, *required* an object providing information strictly related to the app's behavior on a Lampix
  * **lampixVersion**, specifies a range of versions the app is compatible with (e.g "2.0.0", ">= 2.0.0 <= 10.5.1" etc. - as seen on [NPM semver's package](https://docs.npmjs.com/misc/semver#ranges)). The current version is "2.1.0"
  * **showInAppSwitcher**, used to prevent or force its showing in the App Switcher (true, by default)

### Example

```javascript
{
  "name": "trivia",
  "displayName": "Trivia",
  "version": "1.1.1",
  "description": "Test your knowledge on different topics",
  "author": {
    "name": "Your name",
    "email": "your@email.com"
  },
  "lampixConfig": {
    "lampixVersion": "2.1.0",
    "showInAppSwitcher": true
  }
}
```

**NOTE**: You don't need to keep track of two `package.json` files if your app already uses one. You can use the one NPM uses, and Lampix will only use the fields specified above. **It is important to know** that the `getApps()` function returns a list of all the available apps with *ALL* the metadata in `package.json` (at least for now).

## [config.json and schema.json](/application-development/deploying/application-structure#config-and-schema) <a href="#config-and-schema" id="config-and-schema"></a>

`config.json` contains arbitrary information, as the application demands it, whether it's a list of strings, an enormous JSON 20 levels deep or something else. For example, configurable questions for a trivia or a survey style app could belong in config.json

`schema.json` works in tandem with config.json AND my.lampix.com, as it is used to define the structure of the data in config.json and the expected types (numeric, boolean, string, lists, objects, enum values). Based on this file, my.lampix.com will generate a custom form that can be used to edit *config.json* without having to redeploy the app.

**IMPORTANT**: Should the data structure change, that means the application itself will no longer work with the new structure, so the application, config.json and schema.json all need to be updated and the application redeployed.

[`getAppConfig()`](/application-development/lampixjs/api/getappconfig) is the method used to retrieve the data found in config.json. As is tradition, this will be an asynchronous request and the data will be available on the success function of the promise returned.


# Local Deploy

## Prerequisites

* Lampix and a development machine in the same network

## Uploading

* Create a `.zip` file with the structure outlined [here](/application-development/deploying/application-structure)
* Find out Lampix's IP
* Open a web browser on the development machine
* Go to `http://<ip>:8888/apps`
* Click the first button to select a `.zip` file
* Click upload - there should be a message like "Upload successful!"
* Restart Lampix - uploading new apps does not have immediate effect in order to not disrupt currently running experiences&#x20;


# Standard Watchers

* [NeuralNetworkClassifier](/application-development/standard-watchers#neuralnetworkclassifier)
* [MovementBasedSegmenter](/application-development/standard-watchers#movementbasedsegmenter)
* [DepthClassifier](/application-development/standard-watchers#depthclassifier)

## NeuralNetworkClassifier

Uses a convolutional neural network to classify objects in the area watched by this classifier.\
The area is defined by the dimension of the neural network and the center of the bounding box of the contour registered from JS.

### Parameters

```javascript
{
  neural_network_classifier: string // required
}
```

### Usage:

```javascript
{
  name: 'NeuralNetworkClassifier',
  params: {
    neural_network_name: <some_NN_name> // e.g 'fin_all_small'
  }
}
```

## MovementBasedSegmenter

Like [NeuralNetworkClassifier](/application-development/standard-watchers#neuralnetworkclassifier), MovementBasedSegmenter uses a convolutional neural network to classify objects.

MovementBasedSegmenter can detect (i.e locate and classify) multiple objects at a time in the specified watcher shape.

### Parameters

```javascript
{
  neural_network_classifier: string, // required

  // Accept only objects with contours whose circumscribed circle has a radius
  // between "min_radius" and "max_radius". In adition to that, the ratio between the contour
  // and the circumscribed circle must be larger than "min_area_ratio".

  filter_circle: {
    min_radius: integer,
    max_radius: integer,
    min_area_ratio: float // [0, 1] where 1 is a perfect circle
  },

  // Accept only object with contours whose circumscribed rectangle (bounding box)
  // has a ratio of the short/long sides between "min_ratio" and "max_ratio".

  filter_rect: {
    min_ratio: float, // (0, 1], the closer to 1, the more "square" the contour must be
    max_ratio: float, // same as above
  },

  // Threshold value which is used to determine if a pixel is different from the
  // table, therefore it is considered movement.
  // Values in range [1, 255). The higher the value, the more contrast there should be between
  // the table and the object.

  filter_tresh: int
}
```

### Usage:

```javascript
{
  name: 'MovementBasedSegmenter',
  params: {
    ...
  }
}
```

## DepthClassifier

Detects any object on or above the surface defined by the watcher's shape, returning contour information.

### Usage:

```javascript
{
  name: 'DepthClassifier',
  params: {
    frames_until_stable: int // currently experimental
  }
}
```


# Custom Watchers

* [Description](/application-development/lampixcustomwatchers/description)
* [Environment Setup](/application-development/lampixcustomwatchers/environment-setup)
* [Directory Structure](/application-development/lampixcustomwatchers/directory-structure)

## Example

* [End result](/application-development/lampixcustomwatchers/end-result)
* [QRCodeDetector implementation](/application-development/lampixcustomwatchers/qrcodedetector-implementation)


# Description

## Functionality

&#x20;Lampix uses watchers to define how web applications can be interacted with in its context. Each watcher can be thought of as behavior for the device that is translated into data for the web app. The device is capable of triggering the Watchers by using an RGB trigger (it detects the changes in a specific zone, defined by the Watcher) or a DEPTH trigger. The \[Lampix Watchers]\(../app-dev/standard-watchers.md) are actively used for detecting button presses, object movement, object height and object shape. A Custom Watcher brings the possibility of adding completely new behavior for Lampix (e.g., by implementing a \[QRCodeClassifier]\(./QRCodeDetector-implementation.md), we will be able to detect QR Codes and pass the encoded information to the web application).


# Environment Setup

* **Lampix 2.1.4**
* **Python 2.7**
* **OpenCV 3.3.0**

## Available packages

The following list represents the output of the `pip freeze` command.

### (package\_name==version)

```
absl-py==0.5.0
asn1crypto==0.24.0
awscli==1.16.42
backports-abc==0.5
backports.functools-lru-cache==1.5
backports.weakref==1.0.post1
bleach==1.5.0
boto3==1.9.32
botocore==1.12.32
bunch==1.0.1
cachetools==2.1.0
cefpython3==66.0
certifi==2018.8.24
cffi==1.11.5
chardet==3.0.4
colorama==0.3.9
crashreporter==1.13
cryptography==2.3.1
cycler==0.10.0
Cython==0.28.5
decorator==4.3.0
docutils==0.14
enum34==1.1.6
funcsigs==1.0.2
futures==3.2.0
google-api-python-client==1.7.4
google-auth==1.5.1
google-auth-httplib2==0.0.3
h5py==2.8.0
html5lib==0.9999999
httplib2==0.11.3
idna==2.7
ipaddress==1.0.22
Jinja2==2.8
jmespath==0.9.3
kdtree==0.16
Keras==2.2.2
kiwisolver==1.0.1
Markdown==3.0.1
MarkupSafe==1.0
mock==2.0.0
networkx==2.2
numpy==1.15.2
pathlib==1.0.1
pbkdf2==1.3
pbr==4.3.0
peewee==3.7.1
Pillow==5.3.0
protobuf==3.6.1
psutil==5.4.7
pyasn1==0.4.4
pyasn1-modules==0.2.2
pycparser==2.19
pydevd==1.4.0
pyparsing==2.2.2
pyrealsense2==2.16.1.296
python-dateutil==2.7.3
pytz==2018.5
PyWavelets==1.0.1
PyYAML==3.13
pyzbar==0.1.7
requests==2.8.1
requests-futures==0.9.7
rsa==3.4.2
s3transfer==0.1.13
scikit-image==0.13.1
scipy==1.1.0
semver==2.8.1
sha3==0.2.1
Shapely==1.5.13
singledispatch==3.4.0.3
six==1.11.0
subprocess32==3.5.2
tensorflow==1.5.0
tensorflow-tensorboard==1.5.1
tornado==4.5.2
tqdm==4.28.1
uritemplate==3.0.0
urllib3==1.23
v4l2==0.1
visual-logging==1.0
Werkzeug==0.14.1
wifi==0.3.8
```


# Directory Structure

**The working directory is assumed to be the directory of the application for which the Custom Watcher is implemented.**

```
.qr-detector-app
│   app.js
│   index.html    
│   package.json
│   README.md
│   vendor.js
│
└─── watchers
        └─── qr-code-detector
                    └─── qr-code-detector.py
```

**IMPORTANT:** Note that the directory in which the `.py` file is placed, should be named exactly as the `.py` file.


# End result

```python
# The images will be processed using OpenCV and Numpy
import cv2
import numpy

# The decode_qrcode function will be used for decoding the found QR Code
from pyzbar.pyzbar import decode as decode_qrcode

# Used for sending the logs to the ':8888/logs' endpoint
import logging
# For identifying the source of the logs
logger = logging.getLogger("lampix.QRCodeDetector")

from watcher import Watcher
# Will be used to specify the used trigger for this Watcher
from watcher import TriggerType

class QRCodeDetector(Watcher):
    # Calling the base class constructor
    def __init__(self, id, contour):
        # self.contour is the property which holds the corners of the registered area
        Watcher.__init__(self, id, contour)

    def get_vision_trigger(self):
        return TriggerType.TRIGGER_RGB

    def on_movement(self, depth_frame, grey_frame, color_frame, movement_mask):
        # Code that will be executed when an object is placed on the projection surface, 
        # exactly where the watcher was registered
        logger.info('The QRCodeDetector Watcher was triggered')

        x, y, width, height = cv2.boundingRect(self.contour.astype(numpy.int))


        # Cropping the color_frame and getting the ROI (Region of Interest) containing the object that triggered the Watcher
        qr_code_roi = color_frame[y:y + height, x:x + width]

        # Decoding the QR Code and retrieving the data
        qr_code = decode_qrcode(qr_code_roi)[0]
        qr_code_data = qr_code.data

        if len(qr_code_data):
            # The self.report_to_js() method requires a formatted object
            reported_object = self.create_formatted_object(metadata=qr_code_data)
            self.report_to_js(reported_object, method='located')
        else:
            reported_object = self.create_formatted_object(message="QR Code not detected")
            self.report_to_js(reported_object, method='located')

    def on_delete(self):
        """
        Code that will be executed on Watcher's removal
        e.g. closing connections
        """

        pass
```


# QRCodeDetector implementation

* [The Watcher base class](/application-development/lampixcustomwatchers/qrcodedetector-implementation#the-watcher-base-class)
* [Vision trigger](/application-development/lampixcustomwatchers/qrcodedetector-implementation#vision-trigger)
* [Triggering the Watcher](/application-development/lampixcustomwatchers/qrcodedetector-implementation#triggering-the-watcher)
* [Retrieving the Watcher's frame](/application-development/lampixcustomwatchers/qrcodedetector-implementation#retrieving-the-watchers-frame)
* [QR Code detection](/application-development/lampixcustomwatchers/qrcodedetector-implementation#qr-code-detection)
* [Report data to JavaScript](/application-development/lampixcustomwatchers/qrcodedetector-implementation#report-data-to-javascript)

## The Watcher base class

We will start off by extending the Watcher base class that is present in the Lampix Software Stack.

```python
# The images will be processed using OpenCV and Numpy
import cv2
import numpy

# The decode_qrcode function will be used for decoding the found QR Code
from pyzbar.pyzbar import decode as decode_qrcode

# Used for sending the logs to the ':8888/logs' endpoint
import logging
# For identifying the source of the logs
logger = logging.getLogger("lampix.QRCodeDetector")

from lampix_imports.watcher import Watcher
# Will be used to specify the used trigger for this Watcher
from lampix_imports.watcher import TriggerType

class QRCodeDetector(Watcher):
    # Calling the base class constructor
    # The id will be received from JS
    def __init__(self, id, contour):
        # self.contour is the property which holds the Watcher's contour
        Watcher.__init__(self, id, contour)
```

## Vision trigger

The `get_vision_trigger()` method has to be implemented. It will return the trigger type: `TriggerType.TRIGGER_RGB` / `TriggerType.TRIGGER_DEPTH`. If a trigger based on an object's height is needed, `TriggerType.TRIGGER_DEPTH` will be used.

```python
def get_vision_trigger(self):
    return TriggerType.TRIGGER_RGB
```

## Triggering the Watcher

&#x20;A Watcher is triggered by using either an RGB Trigger or a Depth Trigger. In both cases, it is mandatory to overwrite the \`on\_movement()\` method. This method will contain the code that is executed on \_\_EVERY FRAME\_\_ in which an object intersects with the Watcher's zone.

```python
def on_movement(self, depth_frame, gray_frame, color_frame, movement_mask):
    # Code that will be executed when an object is placed on the projection surface, 
    # exactly where the watcher was registered
    logger.info('The QRCodeDetector Watcher was triggered')
```

#### Parameters

* `depth_frame` - Camera frame, as a `numpy.uint8` depth frame - Full HD `numpy.ndarray`
* `gray_frame` - Camera frame, as a grayscale frame - Full HD `numpy.ndarray`
* `color_frame` - Camera frame, as a BGR (blue, green, red) frame, OpenCV compatible - Full&#x20;

  HD `numpy.ndarray`
* `movement_mask` - The mask of the contour that has triggered the Watcher - Full HD `numpy.ndarray`

## [Retrieving the Watcher's frame](/application-development/lampixcustomwatchers/qrcodedetector-implementation#retrieving-the-watchers-frame) <a href="#retrieving-the-watchers-frame" id="retrieving-the-watchers-frame"></a>

&#x20;The frame bounded by the Watcher will be retrieved from the \`self.contour\` property, in the \`on\_movement()\` method, as shown below:

```python
x, y, width, height = cv2.boundingRect(self.contour.astype(numpy.int))
```

* `x`, `y` - the top left X and Y coordinates of the registered zone's bounding box
* `width`, `height` - the width and height of the said bounding box

## QR Code detection

```python
# Cropping the color_frame and getting the ROI (Region of Interest) containing the object that triggered the Watcher
qr_code_roi = color_frame[y:y + height, x:x + width]

# Decoding the QR Code and retrieving the data
qr_code = decode_qrcode(qr_code_roi)[0]
qr_code_data = qr_code.data
```

## Report data to JavaScript

&#x20;By extending \[the Watcher base class]\(#the-watcher-base-class), the method \`self.report\_to\_js(object)\` is inherited. This method will be used to report a list of formatted objects, containing the data that is needed in the web application.

```python
if len(qr_code_data):
    # The self.report_to_js() method requires a list of formatted objects

    # Reporting to JS that a valid QR Code was located
    reported_object = self.create_formatted_object(message="QR Code located.")
    self.report_to_js([reported_object], method='located')

    '''
    Once the additional processing is finished and the data is available (in this case, the data was
    previously retrieved), it is possible to report to JS that the object was successfully classified.
    '''
    reported_object = self.create_formatted_object(metadata=qr_code_data)
    self.report_to_js([reported_object], method='classified')
else:
    reported_object = self.create_formatted_object(message="QR Code not detected.")
    self.report_to_js([reported_object], method='located')
```

The inherited `self.create_formatted_object()` method is documented [here](https://github.com/lampix-org/lampixjs-core/tree/5c8ec2110a5486b5ea1c5bc9e45cbc186ec1745e/docs/LampixCustomWatchers/create-formatted-object.md).

## Extra:

### The motion trigger

&#x20;The Watcher's \`self.trigger\_motion\` member comes as a solution to the problem of not knowing when an object was removed from the Watcher's zone (e.g., It is used for detecing when a finger is removed from a button - simulating an unpress). It is needed as the \`on\_movement()\` method, by default, won't be triggered if an object is not intersecting the Watcher's region of interest.

#### Example:

```python
if len(current_class == 1):  # A finger is present in the Watcher's frame
    self.trigger_motion = True  # Triggering 'on_movement()' again
```

This will essentially call the `on_movement()` method one more time (the member is automatically set to `False`) where the object will be `classified` or `located` once more.

### Multithreaded processing

&#x20;The \`self.deferred\_processing()\` method is used for CPU intensive tasks that need to be run in a separate thread. To be successfully used, the \`self.needs\_deferred\_processing\` member has to be set to \`True\`. \_\_IMPORTANT:\_\_ there is no method of synchronizing the \`on\_movement()\` method call and the call of the \`self.deferred\_processing()\` method. Although, a mechanism based on events can be implemented and used as means of inter-thread communication. #### Example: \`\`\`python self.needs\_deferred\_processing = True def deferred\_processing(): # Code that will be executed in a separate thread # Avoid infinite loops \`\`\`\
&#x20;\### Watcher removal #### Example: \`\`\`python def on\_delete(self): """ Code that will be executed on Watcher's removal e.g. closing connections """ pass \`\`\`


# Community

## App showcases

* [Tower Defense, by Catalin Piscureanu](https://github.com/altcatalin/canvas-td)
  * [See it in action](https://twitter.com/altcatalin/status/1105961945209950210)


# Installation

## Download

* [Windows](https://s3.amazonaws.com/simulator.lampix.com/lampix-simulator-master.exe)
* [macOS](https://s3.amazonaws.com/simulator.lampix.com/lampix-simulator-master.dmg)
* [Linux](https://s3.amazonaws.com/simulator.lampix.com/lampix-simulator-master.AppImage)

### Linux extra step

Make the .AppImage file executable.

* `chmod +x <path-to-AppImage>`
* Run

### Considerations

* **Windows** and **macOS** versions are signed (Linux does not require this)
* **Windows** version may still warn against running the app, in spite of being signed, due to the nature of the certificate used. In short, there are two certificate types for Windows, one of which can be used with a cloud based CI/CD platform and builds trust over time.
* auto updates are enabled on all platforms


# Usage

* [Basics](/lampix-simulator/usage/basics)


# Basics

## Terminology

* `simulator` is used to refer to the main window (the one with the address bar for loading applications)
* `simulation` is used to refer to a simulated application's window

## Accepted protocols

### [`file:`](/lampix-simulator/usage/basics#file) <a href="#file" id="file"></a>

* used to load local files
* can be used via the address bar manually
* can be used by dragging and dropping either a folder with `index.html` in it or an HTML file in the main simulator interface
* `writeJsonToFile` writes in the same directory as the loaded HTML file

#### Notes

Since `localStorage` data is isolated on a per origin basis AND the origin of all URLs using the `file:` protocol is `file://`, data separation cannot be achieved with this protocol. If this is a concern for you, use [the http protocol](/lampix-simulator/usage/basics#http) instead.

#### Example

`file:///home/username/project/super-app/index.html`\
`file:///d:/super-app/index.html`

### [`http(s):`](/lampix-simulator/usage/basics#http) <a href="#http" id="http"></a>

* used to load served web applications (it doesn't matter whether the server is local or remote)
* can be used via the address bar manually
* `writeJsonToFile` writes in the [`user data`](https://github.com/electron/electron/blob/master/docs/api/app.md#appgetpathname) directory, in a folder called `webapps-data`

#### Example

`http://localhost:3000`\
`https://super.remote.app`

### [`simulator:` (experimental)](/lampix-simulator/usage/basics#simulator) <a href="#simulator" id="simulator"></a>

* serves applications in the `webapps` folder found in the [`user data`](https://github.com/electron/electron/blob/master/docs/api/app.md#appgetpathname) directory
* `writeJsonToFile` writes in the [`user data`](https://github.com/electron/electron/blob/master/docs/api/app.md#appgetpathname) directory, in a folder called `webapps-data`

#### Notes

Though `simulator:` URLs resemble [`file:`](/lampix-simulator/usage/basics#file) URLs, these do benefit from separation of `localStorage` data (as the origin is determined to be `simulator://app-name`).

#### Examples

`simulator://super-app`\
`simulator://super-duper-app`

## Selecting watchers and the recognized class

1. In the simulator, open the expansion panel in the middle that says `Simulator <your-url>`
2. Select the watcher name
3. Select the recognized class
4. In the simulation, click inside the area of a matching registered watcher


