diff --git a/repl/src/tutorial/tutorial.mdx b/repl/src/tutorial/tutorial.mdx
index 29363c87..bf9cb6ed 100644
--- a/repl/src/tutorial/tutorial.mdx
+++ b/repl/src/tutorial/tutorial.mdx
@@ -9,3 +9,7 @@ hello!!!!
blablalba
+
+
+
+
\ No newline at end of file
diff --git a/repl/src/useCycle.ts b/repl/src/useCycle.ts
index cd9ea2cb..bb855682 100644
--- a/repl/src/useCycle.ts
+++ b/repl/src/useCycle.ts
@@ -1,8 +1,9 @@
-import { useEffect, useRef, useState } from 'react';
+import { useEffect, useMemo, useRef, useState } from 'react';
import type { ToneEventCallback } from 'tone';
import * as Tone from 'tone';
import { TimeSpan } from '../../strudel.mjs';
import type { Hap } from './types';
+import usePostMessage from './usePostMessage';
export declare interface UseCycleProps {
onEvent: ToneEventCallback;
@@ -61,7 +62,6 @@ function useCycle(props: UseCycleProps) {
}, [onEvent, onSchedule, onQuery, ready]);
const start = async () => {
- console.log('start');
setStarted(true);
await Tone.start();
Tone.Transport.start('+0.1');
@@ -72,7 +72,7 @@ function useCycle(props: UseCycleProps) {
Tone.Transport.pause();
};
const toggle = () => (started ? stop() : start());
- return { start, stop, onEvent, started, toggle, query, activeCycle };
+ return { start, stop, setStarted, onEvent, started, toggle, query, activeCycle };
}
export default useCycle;
diff --git a/repl/src/usePostMessage.ts b/repl/src/usePostMessage.ts
new file mode 100644
index 00000000..aa28d8a9
--- /dev/null
+++ b/repl/src/usePostMessage.ts
@@ -0,0 +1,11 @@
+import { useEffect } from 'react';
+
+function usePostMessage(listener) {
+ useEffect(() => {
+ window.addEventListener('message', listener);
+ return () => window.removeEventListener('message', listener);
+ }, [listener]);
+ return (data) => window.postMessage(data, '*');
+}
+
+export default usePostMessage;
diff --git a/repl/src/useRepl.ts b/repl/src/useRepl.ts
index 5e29d4a1..330d4484 100644
--- a/repl/src/useRepl.ts
+++ b/repl/src/useRepl.ts
@@ -1,11 +1,19 @@
-import { useCallback, useLayoutEffect, useRef, useState } from 'react';
+import { useCallback, useLayoutEffect, useState, useMemo, useEffect } from 'react';
import { isNote } from 'tone';
import { evaluate } from './evaluate';
import { useWebMidi } from './midi';
import type { Pattern } from './types';
import useCycle from './useCycle';
+import usePostMessage from './usePostMessage';
+
+let s4 = () => {
+ return Math.floor((1 + Math.random()) * 0x10000)
+ .toString(16)
+ .substring(1);
+};
function useRepl({ tune, defaultSynth }) {
+ const id = useMemo(() => s4(), []);
const [code, setCode] = useState(tune);
const [activeCode, setActiveCode] = useState();
const [log, setLog] = useState('');
@@ -14,6 +22,7 @@ function useRepl({ tune, defaultSynth }) {
const dirty = code !== activeCode;
const activateCode = (_code = code) => {
!cycle.started && cycle.start();
+ broadcast({ type: 'start', from: id });
if (activeCode && !dirty) {
setError(undefined);
return;
@@ -76,6 +85,14 @@ function useRepl({ tune, defaultSynth }) {
ready: !!pattern,
});
+ const broadcast = usePostMessage(({ data: { from, type } }) => {
+ if (type === 'start' && from !== id) {
+ // console.log('message', from, type);
+ cycle.setStarted(false);
+ setActiveCode(undefined);
+ }
+ });
+
// set active pattern on ctrl+enter
useLayoutEffect(() => {
const handleKeyPress = (e: any) => {