diff --git a/packages/core/pattern.mjs b/packages/core/pattern.mjs
index d69a04a69..5aea0c11a 100644
--- a/packages/core/pattern.mjs
+++ b/packages/core/pattern.mjs
@@ -3359,6 +3359,11 @@ Pattern.prototype.shrinklist = function (amount) {
export const shrinklist = (amount, pat) => pat.shrinklist(amount);
+Pattern.prototype.growlist = function (amount) {
+ return this.shrinklist(amount).reverse();
+};
+export const growlist = (amount, pat) => pat.growlist(amount);
+
/**
* *Experimental*
*
diff --git a/packages/mqtt/mqtt.mjs b/packages/mqtt/mqtt.mjs
index 96659c67b..a84628567 100644
--- a/packages/mqtt/mqtt.mjs
+++ b/packages/mqtt/mqtt.mjs
@@ -4,7 +4,7 @@ Copyright (C) 2022 Strudel contributors - see .
*/
-import { Pattern, isPattern } from '@strudel/core';
+import { Pattern, isPattern, createParams } from '@strudel/core';
import Paho from 'paho-mqtt';
const connections = {};
@@ -118,3 +118,11 @@ Pattern.prototype.mqtt = function (
return hap.setContext({ ...hap.context, onTrigger, dominantTrigger: true });
});
};
+
+
+// This adds the 'move' and 'motor' commands to strudel
+export const { move, motor } = createParams('move', 'motor'); window.move = move; window.motor = motor;
+// This adds the 'robot' command
+Pattern.prototype.robot = function (robot_id, address = 'ws://192.168.8.248:9001/mqtt') {
+ return this.mqtt(undefined, undefined, '/move/' + robot_id, address);
+};
diff --git a/website/src/config.ts b/website/src/config.ts
index eb098d009..12272dbcb 100644
--- a/website/src/config.ts
+++ b/website/src/config.ts
@@ -68,6 +68,7 @@ export const SIDEBAR: Sidebar = {
{ text: 'Pattern Effects', link: 'workshop/pattern-effects' },
{ text: 'Recap', link: 'workshop/recap' },
{ text: 'Workshop in German', link: 'de/workshop/getting-started' },
+ { text: 'Tanglebot workshop', link: 'workshop/motors' },
],
'Making Sound': [
{ text: 'Samples', link: 'learn/samples' },
diff --git a/website/src/pages/workshop/motors.mdx b/website/src/pages/workshop/motors.mdx
new file mode 100644
index 000000000..9d679dee9
--- /dev/null
+++ b/website/src/pages/workshop/motors.mdx
@@ -0,0 +1,119 @@
+---
+title: Movement with Strudel
+layout: ../../layouts/MainLayout.astro
+---
+
+import { MiniRepl } from '@src/docs/MiniRepl';
+import Box from '@components/Box.astro';
+import QA from '@components/QA';
+
+# Controlling motors with Strudel
+
+Strudel is mainly made for making music, but it's possible to pattern other things with it, including motors.
+
+We're going to use a microcontroller for this, called an "[Inventor 2040W](https://shop.pimoroni.com/products/inventor-2040-w)", which is
+a [Pico W](https://shop.pimoroni.com/products/inventor-2040-w?variant=40053063155795) with extra ports added including some for controlling motors.
+
+
+
+## Technical details
+
+Feel free to gloss over these!
+
+- The Inventor 2040W connects to the internet wirelessly, and it can power from a battery or USB. Hopefully the batteries last!
+- It's running [some code](https://github.com/patternclub/alpacalab/blob/main/course/main.py) that listens for messages using an "Internet of Things" network protocol (called MQTT). When it receives a message, it moves a motor.
+- It connects to a small server (running software called 'mosquitto') on Alex's laptop.
+- Strudel can send these messages instead of triggering sounds - that's how we use it to pattern movement.
+
+## First movement
+
+Let's get a motor running!
+
+1. Note the letter drawn on a label on the back of the microcontroller.
+
+2. Plug a battery into your microcontroller.
+
+3. Plug a motor into 'servo' (not motor) plug numbered 1, with the yellow (lightest) cable closest to the '1', and the brown (darkest) cable outward
+
+4. Run the below to set up some values, changing the `x` in 'robot('x')` to the letter on your microcontroller.
+
+
+
+
+
+If you refresh the page, you'll need to change the letter to match your robot again.
+
+If your motor starts moving unexpectedly, someone else might have put your letter in by mistake!
+
+Note that in the above, we start counting motors from '0', so motor 1 on the board is motor 0 in the code.
+
+
+
+## Patterning movement
+
+Many strudel features for playing with sound patterns will work when
+playing with motor patterns. Try playing with the mininotation in the
+`move` command:
+
+
+
+The move instructions are in the range from -90 to 90.
+
+
+ If your motors stop working at some point, and your code looks right, try pressing the 'reset' button on the
+ microcontroller.
+
+
+It's possible to make smooth movements based on different 'waveforms', for example a smooth sinewave:
+
+
+
+The movement is still quite jerky, because the 'segment' command is
+only taking 16 positions from the sinewave. Try increasing it to 32 or 64. It's best not too much higher than that, as the microcontroller
+might get overwhelmed with a backlog of instructions!
+
+
+ Try replacing `sine` with other waveforms: `saw` (sawtooth wave), `tri` (triangular wave) are good, and there is also
+ `rand` (random wave) and `perlin` (a kind of smoothed-out randomness).
+
+
+## Patterning more than one motor
+
+You can pattern the `motor` command separately from the `move` one:
+
+
+
+Alternatively, you can pattern two motors in separate patterns. The below sends the same pattern for the first two motors, but with the second one running slower:
+
+
+
+