diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml
new file mode 100644
index 0000000..28bbb76
--- /dev/null
+++ b/.github/workflows/deploy.yml
@@ -0,0 +1,42 @@
+name: Deploy
+
+env:
+ NODE_VERSION: 17
+ ZOLA_VERSION: 0.16.1
+
+on:
+ push:
+ branches: ["master"]
+ pull_request:
+ branches: ["master"]
+
+ # Allows you to run this workflow manually from the Actions tab
+ workflow_dispatch:
+
+jobs:
+ build:
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v3
+
+ - name: Install Node.js
+ uses: actions/setup-node@v3
+ with:
+ node-version: ${{ env.NODE_VERSION }}
+
+ - name: Install Zola
+ run: |
+ set -x
+ wget -O - \
+ "https://github.com/getzola/zola/releases/download/v${ZOLA_VERSION}/zola-v${ZOLA_VERSION}-x86_64-unknown-linux-gnu.tar.gz" \
+ | sudo tar xzf - -C /usr/local/bin
+
+ - name: Install dependencies
+ run: npm install
+
+ - name: Deploy
+ uses: peaceiris/actions-gh-pages@v3
+ with:
+ github_token: ${{ secrets.GITHUB_TOKEN }}
diff --git a/.gitmodules b/.gitmodules
index 256a87b..a47d3d7 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -4,3 +4,5 @@
[submodule "etro"]
path = etro
url = https://github.com/etro-js/etro.git
+[submodule "etro/"]
+ url = https://github.com/etro-js/etro.git
diff --git a/content/_index.md b/content/_index.md
index 56ecefe..26b2d74 100644
--- a/content/_index.md
+++ b/content/_index.md
@@ -19,13 +19,9 @@ npm install etro
# Usage
-Include it with
```js
-
-```
+import etro from 'etro'
-Let's look at an example:
-```js
var movie = new etro.Movie({ canvas: outputCanvas })
var layer = new etro.layer.Video({ startTime: 0, source: videoElement }) // the layer starts at 0s
movie.addLayer(layer)
diff --git a/content/docs.md b/content/docs.md
index c7dac99..90b6701 100644
--- a/content/docs.md
+++ b/content/docs.md
@@ -6,7 +6,12 @@ weight = 0
Documentation
-
-- [Tutorial](tutorial)
-- [Migrating to v0.8.0](migrating-v0-8-0)
-- [API Reference](api)
+Welcome to the Etro documentation!
+
+- To learn the fundamentals of Etro, check out the [reference](reference)
+section
+- To use Etro in a React app, check out the [Webcam
+Filter](guides/webcam-filter) guide
+- The full API documentation can be found [here](api)
+- To learn how to migrate from Etro v0.7 to v0.8, check out the [migration
+guide](migrating-v0-8-0)
diff --git a/content/docs/guides/webcam-filter.md b/content/docs/guides/webcam-filter.md
new file mode 100644
index 0000000..6cc10ab
--- /dev/null
+++ b/content/docs/guides/webcam-filter.md
@@ -0,0 +1,276 @@
++++
+title = "Webcam Filter"
+template = "page.html"
++++
+
+
Webcam Filter
+
+In this guide, we'll create a simple webcam filter using React and Etro.
+
+## Setting up the React app
+
+First, we'll set up a new React app using
+[create-react-app](https://create-react-app.dev):
+
+```bash
+npx create-react-app webcam-filter
+cd webcam-filter
+```
+
+## Installing Etro
+
+Now, we'll install Etro:
+
+```bash
+npm install etro
+```
+
+## Creating the movie
+
+Now, we'll create a new movie, add a webcam layer to it, and play it.
+
+Create a new file `src/Movie.js`:
+
+```js
+import React, { useEffect, useRef } from "react";
+import etro from "etro";
+
+export default function Movie() {
+ const canvasRef = useRef();
+ const movieRef = useRef();
+
+ useEffect(() => {
+ // Use the canvas ref to get the canvas element
+ const canvas = canvasRef.current;
+
+ // Create a new movie instance
+ const movie = new etro.Movie({ canvas });
+
+ // Get the user's webcam stream
+ navigator.mediaDevices
+ .getUserMedia({ video: true })
+
+ // Create a video element from the stream
+ .then((stream) => {
+ const video = document.createElement("video");
+ video.srcObject = stream;
+ return new Promise((resolve) => {
+ video.onloadedmetadata = () => {
+ resolve(video);
+ };
+ });
+ })
+
+ // Add a video layer to the movie and play it
+ .then((video) => {
+ canvas.width = video.videoWidth;
+ canvas.height = video.videoHeight;
+ const layer = new etro.layer.Video({
+ startTime: 0,
+ source: video,
+ });
+ movie.addLayer(layer);
+ movie.play();
+ movieRef.current = movie;
+ });
+ }, []);
+
+ return ;
+}
+```
+
+Use the `Movie` component in `src/App.js`:
+
+```js
+import React from 'react'
+import Movie from './Movie'
+
+function App() {
+ return (
+
+
+
+ )
+}
+```
+
+## Adding an effect
+
+Now, we'll add an effect to the webcam layer.
+
+Create a new file `src/effect.js`:
+
+```js
+import etro from "etro";
+
+export default class SaturationEffect extends etro.effect.Shader {
+ constructor(options = {}) {
+ super({
+ fragmentSource: `
+ precision highp float;
+
+ uniform sampler2D u_Source;
+ uniform float u_Saturation;
+
+ varying vec2 v_TextureCoord;
+
+ void main() {
+ vec4 color = texture2D(u_Source, v_TextureCoord);
+ float luminance = dot(color.rgb, vec3(0.2126, 0.7152, 0.0722));
+ gl_FragColor = vec4(mix(vec3(luminance), color.rgb, u_Saturation), color.a);
+ }
+ `,
+ uniforms: {
+ saturation: "1f",
+ },
+ });
+
+ this.saturation = options.saturation || 1;
+ }
+}
+```
+
+Next we need to add the effect to the layer. We'll do this in `src/Movie.js`.
+
+Import the effect:
+
+```diff
+ import React, { useEffect, useRef } from "react";
+ import etro from "etro";
++import SaturationEffect from "./effect";
+```
+
+Add a `saturation` property to the `Movie` component:
+
+```diff
+-export default function Movie() {
++export default function Movie({ saturation = 1 }) {
+```
+
+Create a ref to store the effect instance:
+
+```diff
+ export default function Movie() {
+ const canvasRef = useRef();
+ const movieRef = useRef();
++ const effectRef = useRef();
+```
+
+Add the effect to the layer:
+
+```diff
+ const layer = new etro.layer.Video({
+ startTime: 0,
+ source: video,
+ });
++ const effect = new SaturationEffect();
++ layer.addEffect(effect);
++
+ movie.addLayer(layer);
+ movie.play();
+ movieRef.current = movie;
++ effectRef.current = effect;
+ });
+```
+
+Now, update the effect when the `saturation` prop changes:
+
+```diff
+ movie.play();
+
+ effectRef.current = effect;
+ movieRef.current = movie;
+ });
+ }, []);
++
++ useEffect(() => {
++ if (effectRef.current) {
++ effectRef.current.saturation = saturation;
++ }
++ }, [saturation]);
+```
+
+Finally, we can pass a temporary `saturation` value to the `Movie` component in
+`src/App.js`:
+
+```diff
+ import React from 'react'
+ import Movie from './Movie'
+
+ function App() {
+ return (
+
+-
++
+
+ )
+ }
+```
+
+## Changing the saturation
+
+Now, we'll add a slider to change the saturation.
+
+Create a new file `src/Slider.js`:
+
+```js
+import React from "react";
+
+export default function Slider({ value, onChange }) {
+ return (
+ onChange(e.target.value)}
+ />
+ );
+}
+```
+
+Now, import the slider in `src/App.js`:
+
+```diff
+ import React from 'react'
+ import Movie from './Movie'
++import Slider from './Slider'
+
+ function App() {
+ return (
+
+
++
+
+ )
+ }
+```
+
+Next, we'll add a state variable to the app to store the saturation value. This
+value will be modified by the slider and passed to the effect:
+
+```diff
+ import React from 'react'
+ import Movie from './Movie'
+ import Slider from './Slider'
+
+ function App() {
++ const [saturation, setSaturation] = React.useState(1)
++
+ return (
+
+-
+-
++
++
+
+ )
+ }
+```
+
+## Conclusion
+
+That's it! You've created a webcam app with a saturation effect. If you have
+any questions, feel free to ask them in the [etro Discord
+server](https://discord.gg/myrBsQ8Cht).
diff --git a/content/docs/reference.md b/content/docs/reference.md
new file mode 100644
index 0000000..89646aa
--- /dev/null
+++ b/content/docs/reference.md
@@ -0,0 +1,13 @@
++++
+title = "Reference"
+template = "page.html"
++++
+
+
Reference
+
+\+ [Framework Overview](overview)
+\+ [Movies](movies)
+\+ [Layers](layers)
+\+ [Effects](effects)
+\+ [Dynamic Properties](dynamic-properties)
+\+ [Property Filters](property-filters)
diff --git a/content/docs/tutorial/dynamic-properties.md b/content/docs/reference/dynamic-properties.md
similarity index 100%
rename from content/docs/tutorial/dynamic-properties.md
rename to content/docs/reference/dynamic-properties.md
diff --git a/content/docs/tutorial/effects.md b/content/docs/reference/effects.md
similarity index 100%
rename from content/docs/tutorial/effects.md
rename to content/docs/reference/effects.md
diff --git a/content/docs/tutorial/layers.md b/content/docs/reference/layers.md
similarity index 100%
rename from content/docs/tutorial/layers.md
rename to content/docs/reference/layers.md
diff --git a/content/docs/tutorial/movies.md b/content/docs/reference/movies.md
similarity index 89%
rename from content/docs/tutorial/movies.md
rename to content/docs/reference/movies.md
index 1a22ffd..7a1b913 100644
--- a/content/docs/tutorial/movies.md
+++ b/content/docs/reference/movies.md
@@ -23,4 +23,5 @@ movie.height = 200 // also sets the canvas's height to 200
```
Now we have a 200x200 movie, but there are no layers so nothing will be
-rendered. In the [next section](layers), we'll add a video clip to the movie.
+rendered. In the [next section](../layers), we'll add a video clip to the
+movie.
diff --git a/content/docs/tutorial/overview.md b/content/docs/reference/overview.md
similarity index 87%
rename from content/docs/tutorial/overview.md
rename to content/docs/reference/overview.md
index ee9f625..012a229 100644
--- a/content/docs/tutorial/overview.md
+++ b/content/docs/reference/overview.md
@@ -25,7 +25,7 @@ movie.play()
movie.record({ frameRate: 30 }).then(blob => /*do something with `blob`*/)
```
-See the [`Movie` documentation](./movies.md).
+See the [`Movie` documentation](../movies.md).
# Layers
@@ -39,15 +39,15 @@ var layer2 = new etro.layer.Video({ startTime: 3, source: htmlVideoEl })
movie.layers.push(layer1, layer2) // the same as calling movie.addLayer
```
-There are a number of [built-in layers](../api/modules/layer.html), but you can
-also subclass any of them and create your own.
+There are a number of [built-in layers](/docs/api/modules/layer.html), but you
+can also subclass any of them and create your own.
# Effects
Effects alter a layer or movie's output. Currently, only visual effects are
supported. Audio can be manipulated using the [web audio API] (see
-[`Audio`](../api/classes/layer.audio.html)). Etro offers a set of [built-in
-effects](../api/modules/effect.html) that can be used like this:
+[`Audio`](/docs/api/classes/layer.audio.html)). Etro offers a set of [built-in
+effects](/docs/api/modules/effect.html) that can be used like this:
```js
var effect = new etro.effect.Contrast(2.0)
layer.addEffect(effect)
diff --git a/content/docs/tutorial/property-filters.md b/content/docs/reference/property-filters.md
similarity index 100%
rename from content/docs/tutorial/property-filters.md
rename to content/docs/reference/property-filters.md
diff --git a/content/docs/tutorial.md b/content/docs/tutorial.md
deleted file mode 100644
index fa5b699..0000000
--- a/content/docs/tutorial.md
+++ /dev/null
@@ -1,13 +0,0 @@
-+++
-title = "Tutorial"
-template = "page.html"
-+++
-
-