Skip to content

Commit 100985c

Browse files
bwhitmanclaude
andauthored
AMYboard Web: ?play=1 deep links auto-write sketch to simulator (#1092)
Marketing-page sketch links now carry play=1, so the linked sketch starts playing as soon as the visitor clicks "Click anywhere to start" — no need to know about "Write to Simulator". Plain shared links (no play=1) keep the load-only review-first behavior, and the flag is ignored in control mode so nothing is ever auto-written to real hardware. Also fixes a pre-existing tab=code deep-link bug where the 300ms re-select of sketch.py reloaded the FS default into the editor, clobbering the world sketch that had just been loaded. Adds the two missing example sketches to the marketing grid: drum_machine (Gamma9001) and filter_audio_in. Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
1 parent dec1200 commit 100985c

2 files changed

Lines changed: 66 additions & 25 deletions

File tree

tulip/amyboardweb/static/index.html

Lines changed: 28 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -658,83 +658,91 @@ <h2 class="section-title">Hear it in action</h2>
658658
<h2 class="section-title">What it can do</h2>
659659
<p style="font-size:1.2rem; font-weight:500; margin-bottom:2rem; opacity:0.85;">Try out some examples on <a href="/editor/" style="color:#FFDD00; text-decoration:underline; text-underline-offset:3px;">AMYboard online</a>, and send them to your AMYboard in seconds!</p>
660660
<div class="can-do-grid">
661-
<a href="/editor/?mode=simulate&env=acid_generator&user=shorepine&tab=patch" class="can-do-item" style="text-decoration:none; color:#fff;">
661+
<a href="/editor/?mode=simulate&env=acid_generator&user=shorepine&play=1&tab=patch" class="can-do-item" style="text-decoration:none; color:#fff;">
662662
<span class="can-do-title">Generative Acid patch (303 + 808)</span>
663663
<span class="can-do-desc">We set up a TB303 and 808 and play a generative pattern. Twiddle the knobs!!</span>
664664
</a>
665-
<a href="/editor/?mode=simulate&env=woodpiano&user=shorepine&tab=code" class="can-do-item" style="text-decoration:none; color:#fff;">
665+
<a href="/editor/?mode=simulate&env=drum_machine&user=shorepine&play=1&tab=code" class="can-do-item" style="text-decoration:none; color:#fff;">
666+
<span class="can-do-title">Gamma9001 drum machine</span>
667+
<span class="can-do-desc">Three drum channels at once: a main kit rotating through all six Gamma9001 banks, TR-909 hats, and a tabla/shaker percussion layer.</span>
668+
</a>
669+
<a href="/editor/?mode=simulate&env=woodpiano&user=shorepine&play=1&tab=code" class="can-do-item" style="text-decoration:none; color:#fff;">
666670
<span class="can-do-title">A custom 4-op FM synth</span>
667671
<span class="can-do-desc">We set up a custom ALGO patch in Python modeled after the infamous WOOD PIANO preset.</span>
668672
</a>
669-
<a href="/editor/?mode=simulate&env=methodical_mid&user=shorepine&tab=code" class="can-do-item" style="text-decoration:none; color:#fff;">
673+
<a href="/editor/?mode=simulate&env=methodical_mid&user=shorepine&play=1&tab=code" class="can-do-item" style="text-decoration:none; color:#fff;">
670674
<span class="can-do-title">Play a MIDI file</span>
671675
<span class="can-do-desc">We load a MIDI file from disk and play it with a synth patch.</span>
672676
</a>
673-
<a href="/editor/?mode=simulate&env=spacey&user=shorepine&tab=patch" class="can-do-item" style="text-decoration:none; color:#fff;">
677+
<a href="/editor/?mode=simulate&env=spacey&user=shorepine&play=1&tab=patch" class="can-do-item" style="text-decoration:none; color:#fff;">
674678
<span class="can-do-title">A spacey synth patch</span>
675679
<span class="can-do-desc">Global effects (echo, reverb) on top of a slow envelope over PWM.</span>
676680
</a>
677-
<a href="/editor/?mode=simulate&env=cvfilter&user=shorepine&tab=code" class="can-do-item" style="text-decoration:none; color:#fff;">
681+
<a href="/editor/?mode=simulate&env=cvfilter&user=shorepine&play=1&tab=code" class="can-do-item" style="text-decoration:none; color:#fff;">
678682
<span class="can-do-title">Control your filters with CV input</span>
679683
<span class="can-do-desc">We use the AMY CtrlCoef EXT0 to have CV input modulate the filter frequency.</span>
680684
</a>
681-
<a href="/editor/?mode=simulate&env=audiopassthru&user=shorepine&tab=code" class="can-do-item" style="text-decoration:none; color:#fff;">
685+
<a href="/editor/?mode=simulate&env=filter_audio_in&user=shorepine&play=1&tab=code" class="can-do-item" style="text-decoration:none; color:#fff;">
686+
<span class="can-do-title">Filter live audio with CV</span>
687+
<span class="can-do-desc">Runs live audio in through a low-pass filter, with the cutoff frequency controlled by CV1.</span>
688+
</a>
689+
<a href="/editor/?mode=simulate&env=audiopassthru&user=shorepine&play=1&tab=code" class="can-do-item" style="text-decoration:none; color:#fff;">
682690
<span class="can-do-title">Convert between SPDIF and analog audio</span>
683691
<span class="can-do-desc">Set up AMY to pass AUDIO_IN to AUDIO_OUT so that S/PDIF outputs over analog line-out, and vice versa.</span>
684692
</a>
685-
<a href="/editor/?mode=simulate&env=midi2cv&user=shorepine&tab=code" class="can-do-item" style="text-decoration:none; color:#fff;">
693+
<a href="/editor/?mode=simulate&env=midi2cv&user=shorepine&play=1&tab=code" class="can-do-item" style="text-decoration:none; color:#fff;">
686694
<span class="can-do-title">MIDI controller to CV</span>
687695
<span class="can-do-desc">We set up a callback to get MIDI controller data and output scaled CV values.</span>
688696
</a>
689-
<a href="/editor/?mode=simulate&env=dx7filt&user=shorepine&tab=patch" class="can-do-item" style="text-decoration:none; color:#fff;">
697+
<a href="/editor/?mode=simulate&env=dx7filt&user=shorepine&play=1&tab=patch" class="can-do-item" style="text-decoration:none; color:#fff;">
690698
<span class="can-do-title">DX7 setup with filter and reverb</span>
691699
<span class="can-do-desc">Try setting the MIDI CC for VCF freq!</span>
692700
</a>
693-
<a href="/editor/?mode=simulate&env=arp&user=shorepine&tab=code" class="can-do-item" style="text-decoration:none; color:#fff;">
701+
<a href="/editor/?mode=simulate&env=arp&user=shorepine&play=1&tab=code" class="can-do-item" style="text-decoration:none; color:#fff;">
694702
<span class="can-do-title">Arpeggiator</span>
695703
<span class="can-do-desc">Uses MIDI input callbacks and the AMY sequencer to set up an arpeggiator</span>
696704
</a>
697-
<a href="/editor/?mode=simulate&env=audiopassthru&user=shorepine&tab=patch" class="can-do-item" style="text-decoration:none; color:#fff;">
705+
<a href="/editor/?mode=simulate&env=audiopassthru&user=shorepine&play=1&tab=patch" class="can-do-item" style="text-decoration:none; color:#fff;">
698706
<span class="can-do-title">Echo / reverb on live audio</span>
699707
<span class="can-do-desc">Shows how to use AUDIO_IN to add effects on live audio. Turn on "allow audio input" on the web and move the effects!</span>
700708
</a>
701-
<a href="/editor/?mode=simulate&env=chords&user=shorepine&tab=code" class="can-do-item" style="text-decoration:none; color:#fff;">
709+
<a href="/editor/?mode=simulate&env=chords&user=shorepine&play=1&tab=code" class="can-do-item" style="text-decoration:none; color:#fff;">
702710
<span class="can-do-title">Chord computer</span>
703711
<span class="can-do-desc">Plays chords based on a root note.</span>
704712
</a>
705-
<a href="/editor/?mode=simulate&env=wavfile&user=shorepine&tab=code" class="can-do-item" style="text-decoration:none; color:#fff;">
713+
<a href="/editor/?mode=simulate&env=wavfile&user=shorepine&play=1&tab=code" class="can-do-item" style="text-decoration:none; color:#fff;">
706714
<span class="can-do-title">Load a wav file into a sampler</span>
707715
<span class="can-do-desc">Loads a WAVE file from disk (can also be SD card) and plays it back as a sampler.</span>
708716
</a>
709-
<a href="/editor/?mode=simulate&env=xanadu&user=shorepine&tab=code" class="can-do-item" style="text-decoration:none; color:#fff;">
717+
<a href="/editor/?mode=simulate&env=xanadu&user=shorepine&play=1&tab=code" class="can-do-item" style="text-decoration:none; color:#fff;">
710718
<span class="can-do-title">Xanadu</span>
711719
<span class="can-do-desc">Joseph Kung's "Xanadu" for Csound, ported to AMY commands in code.</span>
712720
</a>
713-
<a href="/editor/?mode=simulate&env=wavetable&user=shorepine&tab=patch" class="can-do-item" style="text-decoration:none; color:#fff;">
721+
<a href="/editor/?mode=simulate&env=wavetable&user=shorepine&play=1&tab=patch" class="can-do-item" style="text-decoration:none; color:#fff;">
714722
<span class="can-do-title">Wavetable patch</span>
715723
<span class="can-do-desc">Uses our WAVETABLE oscillator type to play wavetable synthesis.</span>
716724
</a>
717-
<a href="/editor/?mode=simulate&env=preset_selector&user=shorepine&tab=code" class="can-do-item" style="text-decoration:none; color:#fff;">
725+
<a href="/editor/?mode=simulate&env=preset_selector&user=shorepine&play=1&tab=code" class="can-do-item" style="text-decoration:none; color:#fff;">
718726
<span class="can-do-title">Preset Selector</span>
719727
<span class="can-do-desc">Sets up the rotary encoder to cycle through all 257 AMYboard presets.</span>
720728
</a>
721-
<a href="/editor/?mode=simulate&env=piano&user=shorepine&tab=code" class="can-do-item" style="text-decoration:none; color:#fff;">
729+
<a href="/editor/?mode=simulate&env=piano&user=shorepine&play=1&tab=code" class="can-do-item" style="text-decoration:none; color:#fff;">
722730
<span class="can-do-title">Additive-synthesis piano</span>
723731
<span class="can-do-desc">Uses the AMY dpwe piano patch, fully synthesized from partial analysis.</span>
724732
</a>
725-
<a href="/editor/?mode=simulate&env=house_generator&user=shorepine&tab=code" class="can-do-item" style="text-decoration:none; color:#fff;">
733+
<a href="/editor/?mode=simulate&env=house_generator&user=shorepine&play=1&tab=code" class="can-do-item" style="text-decoration:none; color:#fff;">
726734
<span class="can-do-title">Generative House</span>
727735
<span class="can-do-desc">A generative house track with 808 drums, Wood Piano bassline, and piano chords over evolving progressions.</span>
728736
</a>
729-
<a href="/editor/?mode=simulate&env=universal_hair&user=shorepine&tab=code" class="can-do-item" style="text-decoration:none; color:#fff;">
737+
<a href="/editor/?mode=simulate&env=universal_hair&user=shorepine&play=1&tab=code" class="can-do-item" style="text-decoration:none; color:#fff;">
730738
<span class="can-do-title">Evolving Chords</span>
731739
<span class="can-do-desc">Lush evolving chord progressions with layered synths and generative voice leading.</span>
732740
</a>
733-
<a href="/editor/?mode=simulate&env=sampler&user=shorepine&tab=code" class="can-do-item" style="text-decoration:none; color:#fff;">
741+
<a href="/editor/?mode=simulate&env=sampler&user=shorepine&play=1&tab=code" class="can-do-item" style="text-decoration:none; color:#fff;">
734742
<span class="can-do-title">Live MIDI sampler</span>
735743
<span class="can-do-desc">Hold a MIDI key to record up to 5s of audio in, then play it back pitched to the keyboard on synth 1.</span>
736744
</a>
737-
<a href="/editor/?mode=simulate&env=sineclock&user=shorepine&tab=code" class="can-do-item" style="text-decoration:none; color:#fff;">
745+
<a href="/editor/?mode=simulate&env=sineclock&user=shorepine&play=1&tab=code" class="can-do-item" style="text-decoration:none; color:#fff;">
738746
<span class="can-do-title">Sineclock</span>
739747
<span class="can-do-desc">Douglas Repetto's sineclock — chimes the time of day with stacked sine waves.</span>
740748
</a>

tulip/amyboardweb/static/spss.js

Lines changed: 38 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,12 @@ var amyboard_started = false;
1111
// read later in start_audio() after check_url_env_params() has mutated the URL.
1212
// Keeps the default run_sketch() path from racing the deferred world-load.
1313
var _url_env_pending = false;
14+
// Deep-link autoplay (?play=1, used by the marketing page's sketch links):
15+
// once BOTH the deep-link sketch has loaded and audio has started, auto-write
16+
// it to the simulator so it plays without pressing "Write to Simulator".
17+
// Simulate mode only — never auto-writes to real hardware.
18+
var _url_env_autoplay = false;
19+
var _url_env_loaded = false;
1420
var amy_yield_synth_commands = null;
1521
var amy_dump_state_to_string_c = null;
1622

@@ -3785,6 +3791,7 @@ async function import_amyboard_world_file(index) {
37853791

37863792
// Load an AMYboard World environment by username and environment name.
37873793
// Called from URL params: /editor/?env=woodpiano&user=bwhitman
3794+
// Returns true if the sketch was fetched and loaded into the editor.
37883795
async function load_world_environment_by_name(username, envName) {
37893796
try {
37903797
var params = new URLSearchParams();
@@ -3795,7 +3802,7 @@ async function load_world_environment_by_name(username, envName) {
37953802
var data = await response.json();
37963803
if (!data || !Array.isArray(data.items) || !data.items.length) {
37973804
show_alert("Sketch '" + envName + "' by " + username + " not found.");
3798-
return;
3805+
return false;
37993806
}
38003807
// Match by filename stem (e.g. "spacey" matches "spacey.py" or legacy "spacey.tar")
38013808
var item = null;
@@ -3809,7 +3816,7 @@ async function load_world_environment_by_name(username, envName) {
38093816
}
38103817
if (!item) {
38113818
show_alert("Sketch '" + envName + "' by " + username + " not found.");
3812-
return;
3819+
return false;
38133820
}
38143821
var filename = normalize_world_filename(item.filename);
38153822
var packageName = filename.replace(/\.(py|tar)$/, "");
@@ -3836,8 +3843,10 @@ async function load_world_environment_by_name(username, envName) {
38363843
if (codeTabBtn && window.bootstrap) {
38373844
try { new window.bootstrap.Tab(codeTabBtn).show(); } catch (e) {}
38383845
}
3846+
return true;
38393847
} catch (e) {
38403848
show_alert("Failed to load sketch '" + envName + "' by " + username + ".");
3849+
return false;
38413850
}
38423851
}
38433852

@@ -3872,20 +3881,26 @@ function check_url_env_params() {
38723881
var user = params.get("user");
38733882
var tab = params.get("tab"); // "code" or "patch"
38743883
if (env && user) {
3884+
// ?play=1 (marketing-page links): auto-write the sketch to the
3885+
// simulator once it loads and audio starts. Simulate mode only.
3886+
_url_env_autoplay = params.get("play") === "1" && amyboard_mode !== 'control';
38753887
// Show immediate loading toast
38763888
show_world_toast_loading(env);
38773889
// Delay slightly to let micropython init complete
38783890
setTimeout(async function() {
3879-
await load_world_environment_by_name(user, env);
3891+
_url_env_loaded = !!(await load_world_environment_by_name(user, env));
3892+
maybe_autoplay_url_env();
38803893
// Switch to requested tab after environment loads
38813894
if (tab === "code") {
38823895
var codeTab = document.getElementById("environment-tab");
38833896
if (codeTab) {
38843897
new bootstrap.Tab(codeTab).show();
3885-
// Re-select sketch.py and refresh CodeMirror after tab is visible
3898+
// Re-select sketch.py and refresh CodeMirror after tab is visible.
3899+
// loadEditor must be false: true would re-read FS sketch.py into
3900+
// the editor, clobbering the world sketch we just loaded above.
38863901
setTimeout(async function() {
38873902
if (list_environment_files().indexOf("sketch.py") !== -1) {
3888-
await select_environment_file("sketch.py", true);
3903+
await select_environment_file("sketch.py", false);
38893904
}
38903905
if (editor) {
38913906
editor.refresh();
@@ -3902,6 +3917,21 @@ function check_url_env_params() {
39023917
}
39033918
}
39043919

3920+
// Deep-link autoplay: fires the same write_sketch_to_amyboard() the "Write to
3921+
// Simulator" button does, once BOTH the deep-link sketch load and start_audio()
3922+
// have completed. Either can finish first, so both call sites invoke this.
3923+
async function maybe_autoplay_url_env() {
3924+
if (!_url_env_autoplay || !_url_env_loaded || !audio_started) return;
3925+
if (amyboard_mode === 'control') return;
3926+
_url_env_autoplay = false; // one-shot
3927+
try {
3928+
await write_sketch_to_amyboard();
3929+
console.log('deep-link autoplay: sketch written to simulator');
3930+
} catch (e) {
3931+
console.warn('deep-link autoplay failed:', e);
3932+
}
3933+
}
3934+
39053935
// Remembered admin token for reserved usernames (e.g. "shorepine"). Stored in
39063936
// a cookie only after a successful upload so a bad token never gets cached.
39073937
var _ADMIN_TOKEN_COOKIE = "amyboard_world_admin_token";
@@ -6078,6 +6108,9 @@ except Exception as _e:
60786108
// Sync UI knobs from the AMY state that run_sketch just applied.
60796109
try { await sync_channel_knobs_from_synth_to_ui(window.current_synth || 1); } catch (e) {}
60806110
}
6111+
// Deep-link ?play=1: if the sketch already loaded, write it to the simulator
6112+
// now that audio is running (the load-side call fires when audio wins the race).
6113+
await maybe_autoplay_url_env();
60816114
}
60826115

60836116
// ---- wire_code_parser.js ----

0 commit comments

Comments
 (0)