Merge branch 'dev' of https://github.com/Bubobubobubobubo/Topos into dev
This commit is contained in:
174
index.html
174
index.html
@ -19,6 +19,10 @@
|
|||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
details br {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
.app {
|
.app {
|
||||||
max-height: 100vh;
|
max-height: 100vh;
|
||||||
}
|
}
|
||||||
@ -42,43 +46,58 @@
|
|||||||
<header class="py-2 block text-white bg-neutral-900">
|
<header class="py-2 block text-white bg-neutral-900">
|
||||||
<div class="mx-auto flex flex-wrap pl-2 py-1 flex-row items-center">
|
<div class="mx-auto flex flex-wrap pl-2 py-1 flex-row items-center">
|
||||||
<a class="flex title-font font-medium items-center text-black mb-0">
|
<a class="flex title-font font-medium items-center text-black mb-0">
|
||||||
<svg id="share_button" title="Share button" xmlns="http://www.w3.org/2000/svg" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" class="w-10 h-10 text-black p-2 bg-white rounded-full" viewBox="0 0 24 24">
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" class="w-10 h-10 text-black p-2 bg-white rounded-full" viewBox="0 0 24 24">
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" d="M12 6.042A8.967 8.967 0 006 3.75c-1.052 0-2.062.18-3 .512v14.25A8.987 8.987 0 016 18c2.305 0 4.408.867 6 2.292m0-14.25a8.966 8.966 0 016-2.292c1.052 0 2.062.18 3 .512v14.25A8.987 8.987 0 0018 18a8.967 8.967 0 00-6 2.292m0-14.25v14.25" />
|
<path stroke-linecap="round" stroke-linejoin="round" d="M12 6.042A8.967 8.967 0 006 3.75c-1.052 0-2.062.18-3 .512v14.25A8.987 8.987 0 016 18c2.305 0 4.408.867 6 2.292m0-14.25a8.966 8.966 0 016-2.292c1.052 0 2.062.18 3 .512v14.25A8.987 8.987 0 0018 18a8.967 8.967 0 00-6 2.292m0-14.25v14.25" />
|
||||||
</svg>
|
</svg>
|
||||||
|
|
||||||
<span id="universe-viewer" class="hidden xl:block ml-4 text-2xl text-white">Topos</span>
|
<span id="universe-viewer" class="hidden xl:block ml-4 text-2xl text-white">Topos</span>
|
||||||
|
|
||||||
|
|
||||||
</a>
|
</a>
|
||||||
<nav class="py-2 flex flex-wrap items-center text-base absolute right-0">
|
<nav class="py-2 flex flex-wrap items-center text-base absolute right-0">
|
||||||
<a title="Play button" id="play-button-1" class="hover:bg-gray-800 px-2 py-2 rounded-lg">
|
<a title="Play button (Ctrl+P)" id="play-button-1" class="flex flex-row mr-2 hover:bg-gray-800 px-2 py-2 rounded-lg">
|
||||||
<svg class="w-8 h-8" fill="currentColor" viewBox="0 0 14 16">
|
<svg id="play-icon" class="w-7 h-7" fill="currentColor" viewBox="0 0 14 16">
|
||||||
<path d="M0 .984v14.032a1 1 0 0 0 1.506.845l12.006-7.016a.974.974 0 0 0 0-1.69L1.506.139A1 1 0 0 0 0 .984Z"/>
|
<path d="M0 .984v14.032a1 1 0 0 0 1.506.845l12.006-7.016a.974.974 0 0 0 0-1.69L1.506.139A1 1 0 0 0 0 .984Z"/>
|
||||||
</svg>
|
</svg>
|
||||||
|
<svg id="pause-icon" class="hidden w-7 h-7 text-white" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 20 20">
|
||||||
|
<path d="M10 .5a9.5 9.5 0 1 0 9.5 9.5A9.51 9.51 0 0 0 10 .5ZM9 13a1 1 0 0 1-2 0V7a1 1 0 0 1 2 0v6Zm4 0a1 1 0 0 1-2 0V7a1 1 0 0 1 2 0v6Z"/>
|
||||||
|
</svg>
|
||||||
|
<p id="play-label" class="hidden lg:block text-xl pl-2 text-white inline-block">Play</p>
|
||||||
</a>
|
</a>
|
||||||
<a title="Pause button" id="pause-button-1" class="mr-2 hover:text-gray-900 hover:bg-gray-800 px-2 py-2 rounded-lg">
|
<a title="Stop button (Ctrl+R)" id="stop-button-1" class="flex flex-row mr-2 hover:text-gray-900 hover:bg-gray-800 px-2 py-2 rounded-lg">
|
||||||
<svg class="fill-orange-300 w-8 h-8 text-gray-800 text-white" fill="currentColor" viewBox="0 0 20 20">
|
<svg class="w-7 h-7 text-gray-800 dark:text-white" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 20 20">
|
||||||
<path d="M10 .5a9.5 9.5 0 1 0 9.5 9.5A9.51 9.51 0 0 0 10 .5ZM9 13a1 1 0 0 1-2 0V7a1 1 0 0 1 2 0v6Zm4 0a1 1 0 0 1-2 0V7a1 1 0 0 1 2 0v6Z"/>
|
<path d="M10 .5a9.5 9.5 0 1 0 9.5 9.5A9.51 9.51 0 0 0 10 .5Z"/>
|
||||||
</svg>
|
<rect x="6.5" y="6.5" width="7" height="7" fill="black" rx="1" ry="1"/>
|
||||||
|
</svg>
|
||||||
|
<p class="hidden lg:block text-xl pl-2 text-white inline-block">Stop</p>
|
||||||
</a>
|
</a>
|
||||||
<a title="Stop button" id="stop-button-1" class="mr-2 hover:text-gray-900 hover:bg-gray-800 px-2 py-2 rounded-lg">
|
<a title="Eval button (Ctrl+Enter)" id="eval-button-1" class="flex flex-row mr-2 hover:text-gray-900 hover:bg-gray-800 px-2 py-2 rounded-lg">
|
||||||
<svg class="w-8 h-8 text-gray-800 text-white" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 12 16">
|
<svg class="w-7 h-7 text-white" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 18 20">
|
||||||
<path d="M10.819.4a1.974 1.974 0 0 0-2.147.33l-6.5 5.773A2.014 2.014 0 0 0 2 6.7V1a1 1 0 0 0-2 0v14a1 1 0 1 0 2 0V9.3c.055.068.114.133.177.194l6.5 5.773a1.982 1.982 0 0 0 2.147.33A1.977 1.977 0 0 0 12 13.773V2.227A1.977 1.977 0 0 0 10.819.4Z"/>
|
|
||||||
</svg>
|
|
||||||
</a>
|
|
||||||
<a title="Eval button" id="eval-button-1" class="mr-2 hover:text-gray-900 hover:bg-gray-800 px-2 py-2 rounded-lg">
|
|
||||||
<svg class="w-8 h-8 text-white" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 18 20">
|
|
||||||
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M16 1v5h-5M2 19v-5h5m10-4a8 8 0 0 1-14.947 3.97M1 10a8 8 0 0 1 14.947-3.97"/>
|
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M16 1v5h-5M2 19v-5h5m10-4a8 8 0 0 1-14.947 3.97M1 10a8 8 0 0 1 14.947-3.97"/>
|
||||||
</svg>
|
</svg>
|
||||||
|
<p class="hidden lg:block text-xl pl-2 text-white inline-block">Eval</p>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<a title="Clear button" id="clear-button-1" class="mr-2 hover:text-gray-900 hover:bg-gray-800 px-2 py-2 rounded-lg">
|
<a title="Clear button" id="clear-button-1" class="flex flex-row mr-2 hover:text-gray-900 hover:bg-gray-800 px-2 py-2 rounded-lg">
|
||||||
<svg class="w-8 h-8 text-gray-800 text-white" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 17 20">
|
<svg class="w-7 h-7 text-gray-800 dark:text-white" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 18 20">
|
||||||
<path d="M7.958 19.393a7.7 7.7 0 0 1-6.715-3.439c-2.868-4.832 0-9.376.944-10.654l.091-.122a3.286 3.286 0 0 0 .765-3.288A1 1 0 0 1 4.6.8c.133.1.313.212.525.347A10.451 10.451 0 0 1 10.6 9.3c.5-1.06.772-2.213.8-3.385a1 1 0 0 1 1.592-.758c1.636 1.205 4.638 6.081 2.019 10.441a8.177 8.177 0 0 1-7.053 3.795Z"/>
|
<path d="M17 4h-4V2a2 2 0 0 0-2-2H7a2 2 0 0 0-2 2v2H1a1 1 0 0 0 0 2h1v12a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V6h1a1 1 0 1 0 0-2ZM7 2h4v2H7V2Zm1 14a1 1 0 1 1-2 0V8a1 1 0 0 1 2 0v8Zm4 0a1 1 0 0 1-2 0V8a1 1 0 0 1 2 0v8Z"/>
|
||||||
</svg>
|
</svg>
|
||||||
|
<p class="hidden lg:block text-xl pl-2 text-white inline-block">Clear</p>
|
||||||
</a>
|
</a>
|
||||||
<a title="Open Documentation" id="doc-button-1" class="mr-2 hover:text-gray-900 hover:bg-gray-800 px-2 py-2 rounded-lg">
|
|
||||||
<svg class="w-8 h-8 text-white" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 20 18">
|
|
||||||
<path d="M9 1.334C7.06.594 1.646-.84.293.653a1.158 1.158 0 0 0-.293.77v13.973c0 .193.046.383.134.55.088.167.214.306.366.403a.932.932 0 0 0 .5.147c.176 0 .348-.05.5-.147 1.059-.32 6.265.851 7.5 1.65V1.334ZM19.707.653C18.353-.84 12.94.593 11 1.333V18c1.234-.799 6.436-1.968 7.5-1.65a.931.931 0 0 0 .5.147.931.931 0 0 0 .5-.148c.152-.096.279-.235.366-.403.088-.167.134-.357.134-.55V1.423a1.158 1.158 0 0 0-.293-.77Z"/>
|
<a title="Share button" id="share-button" class="flex flex-row mr-2 hover:text-gray-900 hover:bg-gray-800 px-2 py-2 rounded-lg">
|
||||||
</svg>
|
<svg class="w-7 h-7 text-white" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 19 19">
|
||||||
|
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11.013 7.962a3.519 3.519 0 0 0-4.975 0l-3.554 3.554a3.518 3.518 0 0 0 4.975 4.975l.461-.46m-.461-4.515a3.518 3.518 0 0 0 4.975 0l3.553-3.554a3.518 3.518 0 0 0-4.974-4.975L10.3 3.7"/>
|
||||||
|
</svg>
|
||||||
|
<p class="hidden lg:block text-xl pl-2 text-white inline-block">Share</p>
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<a title="Open Documentation (Ctrl+D)" id="doc-button-1" class="flex flex-row mr-2 hover:text-gray-900 hover:bg-gray-800 px-2 py-2 rounded-lg">
|
||||||
|
<p class="hidden lg:block text-xl pr-2 text-white inline-block">Docs</p>
|
||||||
|
<svg class="w-7 h-7 text-gray-800 dark:text-white" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 20 20">
|
||||||
|
<path d="M10 .5a9.5 9.5 0 1 0 9.5 9.5A9.51 9.51 0 0 0 10 .5ZM9.5 4a1.5 1.5 0 1 1 0 3 1.5 1.5 0 0 1 0-3ZM12 15H8a1 1 0 0 1 0-2h1v-3H8a1 1 0 0 1 0-2h2a1 1 0 0 1 1 1v4h1a1 1 0 0 1 0 2Z"/>
|
||||||
|
</svg>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
</nav>
|
</nav>
|
||||||
@ -108,7 +127,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="space-y-2">
|
<div class="space-y-2">
|
||||||
<h2 class="font-semibold dark:text-gray-400">More</h2>
|
<h2 class="font-semibold text-gray-400">More</h2>
|
||||||
<div class="flex flex-col">
|
<div class="flex flex-col">
|
||||||
<a rel="noopener noreferrer" id="docs_about" class="pl-2 pr-2 lg:text-xl text-sm hover:bg-neutral-800 py-1 my-1 rounded-lg">About Topos</a>
|
<a rel="noopener noreferrer" id="docs_about" class="pl-2 pr-2 lg:text-xl text-sm hover:bg-neutral-800 py-1 my-1 rounded-lg">About Topos</a>
|
||||||
</div>
|
</div>
|
||||||
@ -123,7 +142,9 @@
|
|||||||
<div id="app">
|
<div id="app">
|
||||||
<!-- This modal is used for switching between buffers -->
|
<!-- This modal is used for switching between buffers -->
|
||||||
<div id="modal-buffers" class="invisible bg-neutral-900 bg-opacity-50 flex justify-center items-center absolute top-0 right-0 bottom-0 left-0">
|
<div id="modal-buffers" class="invisible bg-neutral-900 bg-opacity-50 flex justify-center items-center absolute top-0 right-0 bottom-0 left-0">
|
||||||
<div id="start-button" class="px-16 py-14 rounded-md text-center">
|
<div id="start-button" class="px-16 pt-4 pb-4 rounded-md text-center bg-white">
|
||||||
|
<p class="text-semibold text-2xl pb-4">Known universes</p>
|
||||||
|
<p id="existing-universes" class="text-xl"></p>
|
||||||
<div id="disclaimer" class="pb-4">
|
<div id="disclaimer" class="pb-4">
|
||||||
<form>
|
<form>
|
||||||
<label for="search" class="mb-2 text-sm font-medium text-gray-900 sr-only text-white">Search</label>
|
<label for="search" class="mb-2 text-sm font-medium text-gray-900 sr-only text-white">Search</label>
|
||||||
@ -133,8 +154,8 @@
|
|||||||
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m19 19-4-4m0-7A7 7 0 1 1 1 8a7 7 0 0 1 14 0Z"/>
|
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="m19 19-4-4m0-7A7 7 0 1 1 1 8a7 7 0 0 1 14 0Z"/>
|
||||||
</svg>
|
</svg>
|
||||||
</div>
|
</div>
|
||||||
<input minlength="2" type="text" id="buffer-search" class="block w-full p-4 pl-10 text-sm text-gray-900 border border-gray-300 outline-0 rounded-lg bg-neutral-900 bg-neutral-900 text-white" placeholder="Buffer..." required>
|
<input name="universe" minlength="2" autocomplete="off" type="text" id="buffer-search" class="block w-full p-4 pl-10 text-sm text-gray-900 border border-gray-800 outline-0 rounded-lg bg-gray-800 text-white" placeholder="Buffer..." required>
|
||||||
<button disabled class="text-black absolute right-2.5 bottom-2.5 bg-white hover:bg-white focus:outline-none font-medium rounded-lg text-sm px-4 py-2">Go</button>
|
<button id="load-universe-button" class="text-black absolute right-2.5 bottom-2.5 bg-white hover:bg-white focus:outline-none font-medium rounded-lg text-sm px-4 py-2">Go</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</div>
|
</div>
|
||||||
@ -162,8 +183,8 @@
|
|||||||
<input id="bordered-radio-1" type="radio" value="" name="bordered-radio" class="w-4 h-4 text-blue-600 bg-gray-100 border-gray-300 focus:ring-blue-500 focus:ring-blue-600 ring-offset-gray-800 focus:ring-2 bg-gray-700 border-gray-600">
|
<input id="bordered-radio-1" type="radio" value="" name="bordered-radio" class="w-4 h-4 text-blue-600 bg-gray-100 border-gray-300 focus:ring-blue-500 focus:ring-blue-600 ring-offset-gray-800 focus:ring-2 bg-gray-700 border-gray-600">
|
||||||
<label id="normal-mode" for="bordered-radio-1" class="w-full py-4 ml-2 text-sm font-medium text-black">Normal Mode</label>
|
<label id="normal-mode" for="bordered-radio-1" class="w-full py-4 ml-2 text-sm font-medium text-black">Normal Mode</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="flex items-center pl-4 border border-gray-200 rounded dark:border-gray-700 w-full">
|
<div class="flex items-center pl-4 border rounded border-gray-700 w-full">
|
||||||
<input checked id="bordered-radio-2" type="radio" value="" name="bordered-radio" class="w-4 h-4 text-blue-600 bg-gray-100 border-gray-300 focus:ring-blue-500 dark:focus:ring-blue-600 dark:ring-offset-gray-800 focus:ring-2 dark:bg-gray-700 dark:border-gray-600">
|
<input checked id="bordered-radio-2" type="radio" value="" name="bordered-radio" class="w-4 h-4 text-blue-600 bg-gray-100 focus:ring-blue-600 ring-offset-gray-800 focus:ring-2 dark:bg-gray-700 dark:border-gray-600">
|
||||||
<label id="vim-mode" for="bordered-radio-2" class="w-full py-4 ml-2 text-sm font-medium text-black">Vim Mode</label>
|
<label id="vim-mode" for="bordered-radio-2" class="w-full py-4 ml-2 text-sm font-medium text-black">Vim Mode</label>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -190,25 +211,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
<!-- This modal pops up when the page is first loaded and then disappears forever -->
|
|
||||||
<div id="modal-container" class="motion-safe:animate-pulse flex min-h-screen flex flex-col">
|
|
||||||
<div id="modal" class="bg-neutral-900 bg-opacity-50 flex justify-center items-center absolute top-0 right-0 bottom-0 left-0">
|
|
||||||
<div id="start-button" class="bg-white px-2 py-2 lg:px-12 lg:py-12 rounded-md text-center">
|
|
||||||
<h1 class="text-xl mb-4 font-bold text-white rounded bg-gray-800 py-4 mb-6">Topos Prototype</h1>
|
|
||||||
<div class="flex ml-0 lg:flex-row landscape:space-y-0 portrait:space-y-2 lg:space-y-4 lg:space-y-0 portrait:flex-col landscape:flex-row lg:flex-col w-auto min-w-screen px-0 lg:space-x-8 landscape:space-x-2">
|
|
||||||
<a href="https://github.com/Bubobubobubobubo/Topos" class="block max-w-sm lg:p-6 border border-gray-200 rounded-lg shadow bg-gray-800 border-gray-700 hover:bg-gray-700">
|
|
||||||
<h5 class="sm:pt-2 mb-2 lg:text-2xl text-xl font-bold tracking-tight text-white">GitHub</h5>
|
|
||||||
<p class="sm:pb-2 font-normal text-white">Get involved in the dev process and or file an issue for a broken feature</p>
|
|
||||||
</a>
|
|
||||||
<a href="https://discord.gg/aPgV7mSFZh" class="block max-w-sm lg:p-6 border border-gray-200 rounded-lg shadow bg-gray-800 border-gray-700 hover:bg-gray-700">
|
|
||||||
<h5 class="sm:pt-2 mb-2 lg:text-2xl text-xl font-bold tracking-tight text-white">Discord</h5>
|
|
||||||
<p class="sm:pb-2 font-normal text-white">Join the community on the official Topos/Sardine Discord Server.</p>
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="flex flex-row max-h-fit">
|
<div class="flex flex-row max-h-fit">
|
||||||
|
|
||||||
<!-- This is a lateral bar that will inherit the header buttons if the window is too small. -->
|
<!-- This is a lateral bar that will inherit the header buttons if the window is too small. -->
|
||||||
@ -220,67 +222,29 @@
|
|||||||
dark:border-neutral-700 border-none"
|
dark:border-neutral-700 border-none"
|
||||||
>
|
>
|
||||||
<nav class="flex flex-col space-y-6">
|
<nav class="flex flex-col space-y-6">
|
||||||
<a title="Local Scripts" id="local-button" class="pl-2 p-1.5 text-gray-700 focus:outline-nones transition-colors duration-200 rounded-lg dark:text-gray-200 hover:bg-gray-800">
|
<a title="Local Scripts (Ctrl + L)" id="local-button" class="pl-2 p-1.5 text-gray-700 focus:outline-nones transition-colors duration-200 rounded-lg text-white hover:bg-gray-800">
|
||||||
<!--
|
|
||||||
<p class="grid font-mono font-semibold text-4xl content-center justify-center">L</p>
|
|
||||||
-->
|
|
||||||
<svg class="w-8 h-8 text-orange-300" fill="currentColor" viewBox="0 0 20 20">
|
<svg class="w-8 h-8 text-orange-300" fill="currentColor" viewBox="0 0 20 20">
|
||||||
<path d="M10 15a5 5 0 1 0 0-10 5 5 0 0 0 0 10Zm0-11a1 1 0 0 0 1-1V1a1 1 0 0 0-2 0v2a1 1 0 0 0 1 1Zm0 12a1 1 0 0 0-1 1v2a1 1 0 1 0 2 0v-2a1 1 0 0 0-1-1ZM4.343 5.757a1 1 0 0 0 1.414-1.414L4.343 2.929a1 1 0 0 0-1.414 1.414l1.414 1.414Zm11.314 8.486a1 1 0 0 0-1.414 1.414l1.414 1.414a1 1 0 0 0 1.414-1.414l-1.414-1.414ZM4 10a1 1 0 0 0-1-1H1a1 1 0 0 0 0 2h2a1 1 0 0 0 1-1Zm15-1h-2a1 1 0 1 0 0 2h2a1 1 0 0 0 0-2ZM4.343 14.243l-1.414 1.414a1 1 0 1 0 1.414 1.414l1.414-1.414a1 1 0 0 0-1.414-1.414ZM14.95 6.05a1 1 0 0 0 .707-.293l1.414-1.414a1 1 0 1 0-1.414-1.414l-1.414 1.414a1 1 0 0 0 .707 1.707Z"/>
|
<path d="M10 15a5 5 0 1 0 0-10 5 5 0 0 0 0 10Zm0-11a1 1 0 0 0 1-1V1a1 1 0 0 0-2 0v2a1 1 0 0 0 1 1Zm0 12a1 1 0 0 0-1 1v2a1 1 0 1 0 2 0v-2a1 1 0 0 0-1-1ZM4.343 5.757a1 1 0 0 0 1.414-1.414L4.343 2.929a1 1 0 0 0-1.414 1.414l1.414 1.414Zm11.314 8.486a1 1 0 0 0-1.414 1.414l1.414 1.414a1 1 0 0 0 1.414-1.414l-1.414-1.414ZM4 10a1 1 0 0 0-1-1H1a1 1 0 0 0 0 2h2a1 1 0 0 0 1-1Zm15-1h-2a1 1 0 1 0 0 2h2a1 1 0 0 0 0-2ZM4.343 14.243l-1.414 1.414a1 1 0 1 0 1.414 1.414l1.414-1.414a1 1 0 0 0-1.414-1.414ZM14.95 6.05a1 1 0 0 0 .707-.293l1.414-1.414a1 1 0 1 0-1.414-1.414l-1.414 1.414a1 1 0 0 0 .707 1.707Z"/>
|
||||||
</svg>
|
</svg>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<a title="Global Script" id="global-button" class="pl-2 p-1.5 text-gray-700 focus:outline-nones transition-colors duration-200 rounded-lg dark:text-gray-200 hover:bg-gray-800">
|
<a title="Global Script (Ctrl + G)" id="global-button" class="pl-2 p-1.5 text-gray-700 focus:outline-nones transition-colors duration-200 rounded-lg text-white hover:bg-gray-800">
|
||||||
<!--
|
|
||||||
<p class="grid font-mono font-semibold text-4xl content-center justify-center">G</p>
|
|
||||||
-->
|
|
||||||
<svg class="w-8 h-8 text-white" fill="currentColor" viewBox="0 0 18 20">
|
<svg class="w-8 h-8 text-white" fill="currentColor" viewBox="0 0 18 20">
|
||||||
<path d="M17.8 13.75a1 1 0 0 0-.859-.5A7.488 7.488 0 0 1 10.52 2a1 1 0 0 0 0-.969A1.035 1.035 0 0 0 9.687.5h-.113a9.5 9.5 0 1 0 8.222 14.247 1 1 0 0 0 .004-.997Z"/>
|
<path d="M17.8 13.75a1 1 0 0 0-.859-.5A7.488 7.488 0 0 1 10.52 2a1 1 0 0 0 0-.969A1.035 1.035 0 0 0 9.687.5h-.113a9.5 9.5 0 1 0 8.222 14.247 1 1 0 0 0 .004-.997Z"/>
|
||||||
</svg>
|
</svg>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<a title="Initialisation Script" id="init-button" class="pl-2 p-1.5 text-gray-700 focus:outline-nones transition-colors duration-200 rounded-lg dark:text-gray-200 bg-gray-800">
|
<a title="Initialisation Script (Ctrl + I)" id="init-button" class="pl-2 p-1.5 text-gray-700 focus:outline-nones transition-colors duration-200 rounded-lg text-white bg-gray-800">
|
||||||
<!--
|
|
||||||
<p class="grid font-mono font-semibold text-4xl content-center justify-center">I</p>
|
|
||||||
-->
|
|
||||||
<svg class="w-8 h-8 text-white " fill="none" viewBox="0 0 21 20">
|
<svg class="w-8 h-8 text-white " fill="none" viewBox="0 0 21 20">
|
||||||
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6.487 1.746c0 4.192 3.592 1.66 4.592 5.754 0 .828 1 1.5 2 1.5s2-.672 2-1.5a1.5 1.5 0 0 1 1.5-1.5h1.5m-16.02.471c4.02 2.248 1.776 4.216 4.878 5.645C10.18 13.61 9 19 9 19m9.366-6h-2.287a3 3 0 0 0-3 3v2m6-8a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z"/>
|
<path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6.487 1.746c0 4.192 3.592 1.66 4.592 5.754 0 .828 1 1.5 2 1.5s2-.672 2-1.5a1.5 1.5 0 0 1 1.5-1.5h1.5m-16.02.471c4.02 2.248 1.776 4.216 4.878 5.645C10.18 13.61 9 19 9 19m9.366-6h-2.287a3 3 0 0 0-3 3v2m6-8a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z"/>
|
||||||
</svg>
|
</svg>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<a title="Project notes" id="note-button" class="pl-2 p-1.5 text-gray-700 focus:outline-nones transition-colors duration-200 rounded-lg dark:text-gray-200 hover:bg-gray-800">
|
<a title="Project notes (Ctrl + N)" id="note-button" class="pl-2 p-1.5 text-gray-700 focus:outline-nones transition-colors duration-200 rounded-lg text-white hover:bg-gray-800">
|
||||||
<svg class="w-8 h-8 text-white" fill="currentColor" viewBox="0 0 16 20">
|
<svg class="w-8 h-8 text-white" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 20 20">
|
||||||
<path d="M16 14V2a2 2 0 0 0-2-2H2a2 2 0 0 0-2 2v15a3 3 0 0 0 3 3h12a1 1 0 0 0 0-2h-1v-2a2 2 0 0 0 2-2ZM4 2h2v12H4V2Zm8 16H3a1 1 0 0 1 0-2h9v2Z"/>
|
<path d="m13.835 7.578-.005.007-7.137 7.137 2.139 2.138 7.143-7.142-2.14-2.14Zm-10.696 3.59 2.139 2.14 7.138-7.137.007-.005-2.141-2.141-7.143 7.143Zm1.433 4.261L2 12.852.051 18.684a1 1 0 0 0 1.265 1.264L7.147 18l-2.575-2.571Zm14.249-14.25a4.03 4.03 0 0 0-5.693 0L11.7 2.611 17.389 8.3l1.432-1.432a4.029 4.029 0 0 0 0-5.689Z"/>
|
||||||
</svg>
|
</svg>
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<!-- Other buttons -->
|
|
||||||
|
|
||||||
<!--
|
|
||||||
<a title="Play button" id="play-button-2" class="pl-2 p-1.5 text-gray-700 focus:outline-nones transition-colors duration-200 rounded-lg dark:text-gray-200 hover:bg-gray-800 sm:landscape:hidden">
|
|
||||||
<svg class="w-8 h-8 text-white block xl:hidden" fill="currentColor" viewBox="0 0 14 16">
|
|
||||||
<path d="M0 .984v14.032a1 1 0 0 0 1.506.845l12.006-7.016a.974.974 0 0 0 0-1.69L1.506.139A1 1 0 0 0 0 .984Z"/>
|
|
||||||
</svg>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<a title="Pause button" id="pause-button-2" class="pl-2 p-1.5 text-gray-700 focus:outline-nones transition-colors duration-200 rounded-lg dark:text-gray-200 hover:bg-gray-800 sm:landscape:hidden">
|
|
||||||
<svg class="fill-orange-300 w-8 h-8 block xl:hidden" fill="currentColor" viewBox="0 0 20 20">
|
|
||||||
<path d="M10 .5a9.5 9.5 0 1 0 9.5 9.5A9.51 9.51 0 0 0 10 .5ZM9 13a1 1 0 0 1-2 0V7a1 1 0 0 1 2 0v6Zm4 0a1 1 0 0 1-2 0V7a1 1 0 0 1 2 0v6Z"/>
|
|
||||||
</svg>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<a title="Stop button" id="stop-button-2" class="pl-2 p-1.5 text-gray-700 focus:outline-nones transition-colors duration-200 rounded-lg dark:text-gray-200 hover:bg-gray-800 sm:landscape:hidden">
|
|
||||||
<svg class="w-8 h-8 text-gray-800 dark:text-white block xl:hidden" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 12 16">
|
|
||||||
<path d="M10.819.4a1.974 1.974 0 0 0-2.147.33l-6.5 5.773A2.014 2.014 0 0 0 2 6.7V1a1 1 0 0 0-2 0v14a1 1 0 1 0 2 0V9.3c.055.068.114.133.177.194l6.5 5.773a1.982 1.982 0 0 0 2.147.33A1.977 1.977 0 0 0 12 13.773V2.227A1.977 1.977 0 0 0 10.819.4Z"/>
|
|
||||||
</svg>
|
|
||||||
</a>
|
|
||||||
|
|
||||||
<a title="Clear button" id="clear-button-2" class="hidden lg:block pl-2 p-1.5 text-gray-700 focus:outline-nones transition-colors duration-200 rounded-lg text-white hover:bg-gray-800">
|
|
||||||
<svg class="w-8 h-8 text-white block xl:hidden" aria-hidden="true" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 17 20">
|
|
||||||
<path d="M7.958 19.393a7.7 7.7 0 0 1-6.715-3.439c-2.868-4.832 0-9.376.944-10.654l.091-.122a3.286 3.286 0 0 0 .765-3.288A1 1 0 0 1 4.6.8c.133.1.313.212.525.347A10.451 10.451 0 0 1 10.6 9.3c.5-1.06.772-2.213.8-3.385a1 1 0 0 1 1.592-.758c1.636 1.205 4.638 6.081 2.019 10.441a8.177 8.177 0 0 1-7.053 3.795Z"/>
|
|
||||||
</svg>
|
|
||||||
</a>
|
|
||||||
-->
|
|
||||||
|
|
||||||
|
|
||||||
<a title="Open settings" id="settings-button" class="lg:block sm:hidden p-2 text-gray-200 ml-1 absolute bottom-2 focus:outline-nones transition-colors duration-200 rounded-lg text-gray-200 bg-gray-800">
|
<a title="Open settings" id="settings-button" class="lg:block sm:hidden p-2 text-gray-200 ml-1 absolute bottom-2 focus:outline-nones transition-colors duration-200 rounded-lg text-gray-200 bg-gray-800">
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
|
||||||
@ -294,36 +258,36 @@
|
|||||||
<div class="min-w-screen flex grow flex-col">
|
<div class="min-w-screen flex grow flex-col">
|
||||||
<ul id="local-script-tabs" class="text-xl flex text-sm font-medium text-center text-white bg-neutral-900 space-x-2 lg:space-x-8">
|
<ul id="local-script-tabs" class="text-xl flex text-sm font-medium text-center text-white bg-neutral-900 space-x-2 lg:space-x-8">
|
||||||
<li class="pl-5">
|
<li class="pl-5">
|
||||||
<a title="Local Script 1" id="tab-1" class="bg-orange-300 inline-block px-4 py-1 text-white hover:bg-gray-800">1</a>
|
<a title="Local Script 1 (F1)" id="tab-1" class="bg-orange-300 inline-block px-4 py-1 text-white hover:bg-gray-800">1</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="">
|
<li class="">
|
||||||
<a title="Local Script 2" id="tab-2" class="inline-block px-4 py-1 hover:bg-gray-800">2</a>
|
<a title="Local Script 2 (F2)" id="tab-2" class="inline-block px-4 py-1 hover:bg-gray-800">2</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="">
|
<li class="">
|
||||||
<a title="Local Script 3" id="tab-3" class="inline-block px-4 py-1 hover:bg-gray-800">3</a>
|
<a title="Local Script 3 (F3)" id="tab-3" class="inline-block px-4 py-1 hover:bg-gray-800">3</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="">
|
<li class="">
|
||||||
<a title="Local Script 4" id="tab-4" class="inline-block px-4 py-1 hover:bg-gray-800">4</a>
|
<a title="Local Script 4 (F4)" id="tab-4" class="inline-block px-4 py-1 hover:bg-gray-800">4</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="">
|
<li class="">
|
||||||
<a title="Local Script 5" id="tab-5" class="inline-block px-4 py-1 hover:bg-gray-800">5</a>
|
<a title="Local Script 5 (F5)" id="tab-5" class="inline-block px-4 py-1 hover:bg-gray-800">5</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="">
|
<li class="">
|
||||||
<a title="Local Script 6" id="tab-6" class="inline-block px-4 py-1 hover:bg-gray-800">6</a>
|
<a title="Local Script 6 (F6)" id="tab-6" class="inline-block px-4 py-1 hover:bg-gray-800">6</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="">
|
<li class="">
|
||||||
<a title="Local Script 7" id="tab-7" class="inline-block px-4 py-1 hover:bg-gray-800">7</a>
|
<a title="Local Script 7 (F7)" id="tab-7" class="inline-block px-4 py-1 hover:bg-gray-800">7</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="">
|
<li class="">
|
||||||
<a title="Local Script 8" id="tab-8" class="inline-block px-4 py-1 hover:bg-gray-800">8</a>
|
<a title="Local Script 8 (F8)" id="tab-8" class="inline-block px-4 py-1 hover:bg-gray-800">8</a>
|
||||||
</li>
|
</li>
|
||||||
<li class="">
|
<li class="">
|
||||||
<a title="Local Script 9" id="tab-9" class="inline-block px-4 py-1 hover:bg-gray-800">9</a>
|
<a title="Local Script 9 (F9)" id="tab-9" class="inline-block px-4 py-1 hover:bg-gray-800">9</a>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
</ul>
|
</ul>
|
||||||
<!-- Here comes the editor itself -->
|
<!-- Here comes the editor itself -->
|
||||||
<div id="editor" class="invisible relative flex flex-row h-screen overflow-y-hidden">
|
<div id="editor" class="relative flex flex-row h-screen overflow-y-hidden">
|
||||||
</div>
|
</div>
|
||||||
<p id="error_line" class="hidden text-red-400 w-screen bg-neutral-900 font-mono absolute bottom-0 pl-2 py-2">Hello kids</p>
|
<p id="error_line" class="hidden text-red-400 w-screen bg-neutral-900 font-mono absolute bottom-0 pl-2 py-2">Hello kids</p>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
67
src/API.ts
67
src/API.ts
@ -41,6 +41,7 @@ export class UserAPI {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
private variables: { [key: string]: any } = {};
|
private variables: { [key: string]: any } = {};
|
||||||
|
public codeExamples: { [key: string]: string } = {};
|
||||||
private counters: { [key: string]: any } = {};
|
private counters: { [key: string]: any } = {};
|
||||||
private _drunk: DrunkWalk = new DrunkWalk(-100, 100, false);
|
private _drunk: DrunkWalk = new DrunkWalk(-100, 100, false);
|
||||||
public randomGen = Math.random;
|
public randomGen = Math.random;
|
||||||
@ -52,9 +53,32 @@ export class UserAPI {
|
|||||||
MidiConnection: MidiConnection = new MidiConnection();
|
MidiConnection: MidiConnection = new MidiConnection();
|
||||||
load: samples;
|
load: samples;
|
||||||
|
|
||||||
constructor(public app: Editor) {
|
constructor(public app: Editor) {}
|
||||||
//this.load = samples("github:tidalcycles/Dirt-Samples/master");
|
|
||||||
}
|
_loadUniverseFromInterface = (universe: string) => {
|
||||||
|
this.app.loadUniverse(universe as string);
|
||||||
|
this.app.openBuffersModal();
|
||||||
|
}
|
||||||
|
|
||||||
|
_deleteUniverseFromInterface = (universe: string) => {
|
||||||
|
delete this.app.universes[universe];
|
||||||
|
this.app.settings.saveApplicationToLocalStorage(
|
||||||
|
this.app.universes,
|
||||||
|
this.app.settings
|
||||||
|
);
|
||||||
|
this.app.openBuffersModal();
|
||||||
|
}
|
||||||
|
|
||||||
|
_playDocExample = (code?: string) => {
|
||||||
|
this.play();
|
||||||
|
console.log("Executing documentation example: " + this.app.selectedExample);
|
||||||
|
this.app.universes[this.app.selected_universe as string].global.candidate =
|
||||||
|
code ? code : (this.app.selectedExample as string);
|
||||||
|
tryEvaluate(
|
||||||
|
this.app,
|
||||||
|
this.app.universes[this.app.selected_universe as string].global
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
_all_samples = (): object => {
|
_all_samples = (): object => {
|
||||||
return soundMap.get();
|
return soundMap.get();
|
||||||
@ -103,6 +127,11 @@ export class UserAPI {
|
|||||||
// Mouse functions
|
// Mouse functions
|
||||||
// =============================================================
|
// =============================================================
|
||||||
|
|
||||||
|
onmousemove = (e: MouseEvent) => {
|
||||||
|
this.app._mouseX = e.clientX;
|
||||||
|
this.app._mouseY = e.clientY;
|
||||||
|
}
|
||||||
|
|
||||||
public mouseX = (): number => {
|
public mouseX = (): number => {
|
||||||
/**
|
/**
|
||||||
* @returns The current x position of the mouse
|
* @returns The current x position of the mouse
|
||||||
@ -1225,4 +1254,36 @@ export class UserAPI {
|
|||||||
// is evaluated. This is useful for slowing down the script, or speeding it up. The default
|
// is evaluated. This is useful for slowing down the script, or speeding it up. The default
|
||||||
// would be 1.0, which is the current rate (very speedy).
|
// would be 1.0, which is the current rate (very speedy).
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// =============================================================
|
||||||
|
// Legacy functions
|
||||||
|
// =============================================================
|
||||||
|
|
||||||
|
public divseq = (...args: any): any => {
|
||||||
|
const chunk_size = args[0]; // Get the first argument (chunk size)
|
||||||
|
const elements = args.slice(1); // Get the rest of the arguments as an array
|
||||||
|
const timepos = this.epulse();
|
||||||
|
const slice_count = Math.floor(
|
||||||
|
timepos / Math.floor(chunk_size * this.ppqn())
|
||||||
|
);
|
||||||
|
return elements[slice_count % elements.length];
|
||||||
|
};
|
||||||
|
|
||||||
|
public seqbeat = <T>(...array: T[]): T => {
|
||||||
|
/**
|
||||||
|
* Returns an element from an array based on the current beat.
|
||||||
|
*
|
||||||
|
* @param array - The array of values to pick from
|
||||||
|
*/
|
||||||
|
return array[this.ebeat() % array.length];
|
||||||
|
};
|
||||||
|
|
||||||
|
public seqbar = <T>(...array: T[]): T => {
|
||||||
|
/**
|
||||||
|
* Returns an element from an array based on the current bar.
|
||||||
|
*
|
||||||
|
* @param array - The array of values to pick from
|
||||||
|
*/
|
||||||
|
return array[(this.app.clock.time_position.bar + 1) % array.length];
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
177
src/main.ts
177
src/main.ts
@ -1,6 +1,5 @@
|
|||||||
import {
|
import {
|
||||||
uniqueNamesGenerator,
|
uniqueNamesGenerator,
|
||||||
adjectives,
|
|
||||||
colors,
|
colors,
|
||||||
animals,
|
animals,
|
||||||
} from "unique-names-generator";
|
} from "unique-names-generator";
|
||||||
@ -33,16 +32,19 @@ import showdown from "showdown";
|
|||||||
showdown.setFlavor("github");
|
showdown.setFlavor("github");
|
||||||
import showdownHighlight from "showdown-highlight";
|
import showdownHighlight from "showdown-highlight";
|
||||||
const classMap = {
|
const classMap = {
|
||||||
h1: "text-white lg:text-4xl text-xl lg:ml-4 lg:mx-4 mx-2 lg:my-4 my-2 lg:mb-8 mb-4 bg-neutral-900 rounded-lg py-2 px-2",
|
h1: "text-white lg:text-4xl text-xl lg:ml-4 lg:mx-4 mx-2 lg:my-4 my-2 lg:mb-4 mb-4 bg-neutral-900 rounded-lg py-2 px-2",
|
||||||
h2: "text-white lg:text-3xl text-xl lg:ml-4 lg:mx-4 mx-2 lg:my-4 my-2 lg:mb-8 mb-4 bg-neutral-900 rounded-lg py-2 px-2",
|
h2: "text-white lg:text-3xl text-xl lg:ml-4 lg:mx-4 mx-2 lg:my-4 my-2 lg:mb-4 mb-4 bg-neutral-900 rounded-lg py-2 px-2",
|
||||||
ul: "text-underline pl-6",
|
ul: "text-underline pl-6",
|
||||||
li: "list-disc lg:text-2xl text-base text-white lg:mx-4 mx-2 my-4 my-2 leading-normal",
|
li: "list-disc lg:text-2xl text-base text-white lg:mx-4 mx-2 my-4 my-2 leading-normal",
|
||||||
p: "lg:text-2xl text-base text-white lg:mx-4 mx-2 my-4 leading-normal",
|
p: "lg:text-2xl text-base text-white lg:mx-6 mx-2 my-4 leading-normal",
|
||||||
a: "lg:text-2xl text-base text-orange-300",
|
a: "lg:text-2xl text-base text-orange-300",
|
||||||
code: "lg:my-4 sm:my-1 text-base lg:text-xl block whitespace-pre overflow-x-hidden",
|
code: "lg:my-4 sm:my-1 text-base lg:text-xl block whitespace-pre overflow-x-hidden",
|
||||||
icode:
|
icode:
|
||||||
"lg:my-4 my-1 lg:text-xl sm:text-xs text-white font-mono bg-neutral-600",
|
"lg:my-1 my-1 lg:text-xl sm:text-xs text-white font-mono bg-neutral-600",
|
||||||
blockquote: "text-neutral-200 border-l-4 border-neutral-500 pl-4 my-4 mx-4",
|
blockquote: "text-neutral-200 border-l-4 border-neutral-500 pl-4 my-4 mx-4",
|
||||||
|
details:
|
||||||
|
"lg:mx-12 py-2 px-6 lg:text-2xl text-white rounded-lg bg-neutral-600",
|
||||||
|
summary: "font-semibold text-xl",
|
||||||
table:
|
table:
|
||||||
"justify-center lg:my-8 my-2 lg:mx-8 mx-2 lg:text-2xl text-base w-full text-left text-white border-collapse",
|
"justify-center lg:my-8 my-2 lg:mx-8 mx-2 lg:text-2xl text-base w-full text-left text-white border-collapse",
|
||||||
thead:
|
thead:
|
||||||
@ -58,8 +60,6 @@ const bindings = Object.keys(classMap).map((key) => ({
|
|||||||
replace: (match, p1) => `<${key} class="${classMap[key]}" ${p1}>`,
|
replace: (match, p1) => `<${key} class="${classMap[key]}" ${p1}>`,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// Importing the documentation from separate files in the ./src/documentation/* folder
|
|
||||||
|
|
||||||
export class Editor {
|
export class Editor {
|
||||||
universes: Universes = template_universes;
|
universes: Universes = template_universes;
|
||||||
selected_universe: string;
|
selected_universe: string;
|
||||||
@ -76,6 +76,7 @@ export class Editor {
|
|||||||
userPlugins: Extension[] = [];
|
userPlugins: Extension[] = [];
|
||||||
state: EditorState;
|
state: EditorState;
|
||||||
api: UserAPI;
|
api: UserAPI;
|
||||||
|
selectedExample: string | null = "";
|
||||||
docs: { [key: string]: string } = {};
|
docs: { [key: string]: string } = {};
|
||||||
|
|
||||||
// Audio stuff
|
// Audio stuff
|
||||||
@ -83,6 +84,7 @@ export class Editor {
|
|||||||
view: EditorView;
|
view: EditorView;
|
||||||
clock: Clock;
|
clock: Clock;
|
||||||
manualPlay: boolean = false;
|
manualPlay: boolean = false;
|
||||||
|
isPlaying: boolean = false;
|
||||||
|
|
||||||
// Mouse position
|
// Mouse position
|
||||||
public _mouseX: number = 0;
|
public _mouseX: number = 0;
|
||||||
@ -91,11 +93,6 @@ export class Editor {
|
|||||||
// Transport elements
|
// Transport elements
|
||||||
play_buttons: HTMLButtonElement[] = [
|
play_buttons: HTMLButtonElement[] = [
|
||||||
document.getElementById("play-button-1") as HTMLButtonElement,
|
document.getElementById("play-button-1") as HTMLButtonElement,
|
||||||
//document.getElementById("play-button-2") as HTMLButtonElement,
|
|
||||||
];
|
|
||||||
pause_buttons: HTMLButtonElement[] = [
|
|
||||||
document.getElementById("pause-button-1") as HTMLButtonElement,
|
|
||||||
//document.getElementById("pause-button-2") as HTMLButtonElement,
|
|
||||||
];
|
];
|
||||||
stop_buttons: HTMLButtonElement[] = [
|
stop_buttons: HTMLButtonElement[] = [
|
||||||
document.getElementById("stop-button-1") as HTMLButtonElement,
|
document.getElementById("stop-button-1") as HTMLButtonElement,
|
||||||
@ -105,6 +102,8 @@ export class Editor {
|
|||||||
document.getElementById("clear-button-1") as HTMLButtonElement,
|
document.getElementById("clear-button-1") as HTMLButtonElement,
|
||||||
//document.getElementById("clear-button-2") as HTMLButtonElement,
|
//document.getElementById("clear-button-2") as HTMLButtonElement,
|
||||||
];
|
];
|
||||||
|
load_universe_button: HTMLButtonElement = document.getElementById("load-universe-button") as HTMLButtonElement;
|
||||||
|
|
||||||
documentation_button: HTMLButtonElement = document.getElementById(
|
documentation_button: HTMLButtonElement = document.getElementById(
|
||||||
"doc-button-1"
|
"doc-button-1"
|
||||||
) as HTMLButtonElement;
|
) as HTMLButtonElement;
|
||||||
@ -171,7 +170,7 @@ export class Editor {
|
|||||||
|
|
||||||
// Share button
|
// Share button
|
||||||
share_button: HTMLElement = document.getElementById(
|
share_button: HTMLElement = document.getElementById(
|
||||||
"share_button"
|
"share-button"
|
||||||
) as HTMLElement;
|
) as HTMLElement;
|
||||||
|
|
||||||
// Error line
|
// Error line
|
||||||
@ -238,9 +237,12 @@ export class Editor {
|
|||||||
|
|
||||||
// ================================================================================
|
// ================================================================================
|
||||||
// Building the documentation
|
// Building the documentation
|
||||||
loadSamples().then(() => {
|
// loadSamples().then(() => {
|
||||||
this.docs = documentation_factory(this);
|
// this.docs = documentation_factory(this);
|
||||||
});
|
// });
|
||||||
|
let pre_loading = async () => { await loadSamples(); };
|
||||||
|
pre_loading();
|
||||||
|
this.docs = documentation_factory(this);
|
||||||
// ================================================================================
|
// ================================================================================
|
||||||
|
|
||||||
// ================================================================================
|
// ================================================================================
|
||||||
@ -305,6 +307,18 @@ export class Editor {
|
|||||||
// This is the modal to switch between universes
|
// This is the modal to switch between universes
|
||||||
if (event.ctrlKey && event.key === "b") {
|
if (event.ctrlKey && event.key === "b") {
|
||||||
this.hideDocumentation();
|
this.hideDocumentation();
|
||||||
|
let existing_universes = document.getElementById("existing-universes");
|
||||||
|
let known_universes = Object.keys(this.universes);
|
||||||
|
let final_html = "<ul class='lg:h-80 lg:w-80 lg:pb-2 lg:pt-2 overflow-y-scroll text-white lg:mb-4 border rounded-lg bg-gray-800'>";
|
||||||
|
known_universes.forEach((name) => {
|
||||||
|
final_html += `
|
||||||
|
<li onclick="_loadUniverseFromInterface('${name}')" class="hover:fill-black hover:bg-white py-2 hover:text-black flex justify-between px-4">
|
||||||
|
<p >${name}</p>
|
||||||
|
<button onclick=_deleteUniverseFromInterface('${name}')>🗑</button>
|
||||||
|
</li>`;
|
||||||
|
});
|
||||||
|
final_html = final_html + "</ul>";
|
||||||
|
existing_universes!.innerHTML = final_html;
|
||||||
this.openBuffersModal();
|
this.openBuffersModal();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -396,9 +410,16 @@ export class Editor {
|
|||||||
|
|
||||||
this.play_buttons.forEach((button) => {
|
this.play_buttons.forEach((button) => {
|
||||||
button.addEventListener("click", () => {
|
button.addEventListener("click", () => {
|
||||||
this.setButtonHighlighting("play", true);
|
if (this.isPlaying) {
|
||||||
this.clock.start();
|
this.setButtonHighlighting("pause", true);
|
||||||
});
|
this.isPlaying = !this.isPlaying;
|
||||||
|
this.clock.pause();
|
||||||
|
} else {
|
||||||
|
this.setButtonHighlighting("play", true);
|
||||||
|
this.isPlaying = !this.isPlaying;
|
||||||
|
this.clock.start();
|
||||||
|
}
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
this.clear_buttons.forEach((button) => {
|
this.clear_buttons.forEach((button) => {
|
||||||
@ -415,17 +436,23 @@ export class Editor {
|
|||||||
this.showDocumentation();
|
this.showDocumentation();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
this.load_universe_button.addEventListener("click", () => {
|
||||||
|
let query = this.buffer_search.value;
|
||||||
|
if (query.length > 2 && query.length < 20 && !query.includes(" ")) {
|
||||||
|
this.loadUniverse(query);
|
||||||
|
this.settings.selected_universe = query;
|
||||||
|
this.buffer_search.value = "";
|
||||||
|
this.closeBuffersModal();
|
||||||
|
this.view.focus();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
this.eval_button.addEventListener("click", () => {
|
this.eval_button.addEventListener("click", () => {
|
||||||
this.currentFile().candidate = this.view.state.doc.toString();
|
this.currentFile().candidate = this.view.state.doc.toString();
|
||||||
this.flashBackground("#2d313d", 200);
|
this.flashBackground("#2d313d", 200);
|
||||||
});
|
});
|
||||||
|
|
||||||
this.pause_buttons.forEach((button) => {
|
|
||||||
button.addEventListener("click", () => {
|
|
||||||
this.setButtonHighlighting("pause", true);
|
|
||||||
this.clock.pause();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
this.stop_buttons.forEach((button) => {
|
this.stop_buttons.forEach((button) => {
|
||||||
button.addEventListener("click", () => {
|
button.addEventListener("click", () => {
|
||||||
@ -485,11 +512,6 @@ export class Editor {
|
|||||||
});
|
});
|
||||||
|
|
||||||
this.share_button.addEventListener("click", () => {
|
this.share_button.addEventListener("click", () => {
|
||||||
this.share_button.classList.add("animate-spin");
|
|
||||||
setInterval(
|
|
||||||
() => this.share_button.classList.remove("animate-spin"),
|
|
||||||
1000
|
|
||||||
);
|
|
||||||
// trigger a manual save
|
// trigger a manual save
|
||||||
this.currentFile().candidate = app.view.state.doc.toString();
|
this.currentFile().candidate = app.view.state.doc.toString();
|
||||||
this.currentFile().committed = app.view.state.doc.toString();
|
this.currentFile().committed = app.view.state.doc.toString();
|
||||||
@ -550,9 +572,18 @@ export class Editor {
|
|||||||
"about",
|
"about",
|
||||||
].forEach((e) => {
|
].forEach((e) => {
|
||||||
let name = `docs_` + e;
|
let name = `docs_` + e;
|
||||||
document.getElementById(name)!.addEventListener("click", () => {
|
document.getElementById(name)!.addEventListener("click", async () => {
|
||||||
this.currentDocumentationPane = e;
|
if (name !== "docs_samples") {
|
||||||
this.updateDocumentationContent();
|
this.currentDocumentationPane = e;
|
||||||
|
this.updateDocumentationContent();
|
||||||
|
} else {
|
||||||
|
console.log('Loading samples!');
|
||||||
|
await loadSamples().then(() => {
|
||||||
|
this.docs = documentation_factory(this)
|
||||||
|
this.currentDocumentationPane = e;
|
||||||
|
this.updateDocumentationContent();
|
||||||
|
});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -597,7 +628,8 @@ export class Editor {
|
|||||||
if (universeParam !== null) {
|
if (universeParam !== null) {
|
||||||
new_universe = JSON.parse(atob(universeParam));
|
new_universe = JSON.parse(atob(universeParam));
|
||||||
const randomName: string = uniqueNamesGenerator({
|
const randomName: string = uniqueNamesGenerator({
|
||||||
dictionaries: [adjectives, colors, animals],
|
length: 2, separator: '_',
|
||||||
|
dictionaries: [colors, animals],
|
||||||
});
|
});
|
||||||
this.loadUniverse(randomName, new_universe["universe"]);
|
this.loadUniverse(randomName, new_universe["universe"]);
|
||||||
this.emptyUrl();
|
this.emptyUrl();
|
||||||
@ -675,13 +707,8 @@ export class Editor {
|
|||||||
const converted_markdown = converter.makeHtml(
|
const converted_markdown = converter.makeHtml(
|
||||||
this.docs[this.currentDocumentationPane]
|
this.docs[this.currentDocumentationPane]
|
||||||
);
|
);
|
||||||
function wrapCodeWithPre(inputString: string): string {
|
|
||||||
let newString = inputString.replace(/<code>/g, "<pre><code>");
|
|
||||||
newString = newString.replace(/<\/code>/g, "</code></pre>");
|
|
||||||
return newString;
|
|
||||||
}
|
|
||||||
document.getElementById("documentation-content")!.innerHTML =
|
document.getElementById("documentation-content")!.innerHTML =
|
||||||
wrapCodeWithPre(converted_markdown);
|
converted_markdown;
|
||||||
}
|
}
|
||||||
|
|
||||||
changeToLocalBuffer(i: number) {
|
changeToLocalBuffer(i: number) {
|
||||||
@ -766,10 +793,26 @@ export class Editor {
|
|||||||
button: "play" | "pause" | "stop" | "clear",
|
button: "play" | "pause" | "stop" | "clear",
|
||||||
highlight: boolean
|
highlight: boolean
|
||||||
) {
|
) {
|
||||||
|
document.getElementById('play-label')!.textContent = button !== "pause" ? "Pause" : "Play";
|
||||||
|
if (button !== "pause") {
|
||||||
|
document.getElementById('pause-icon')!.classList.remove('hidden');
|
||||||
|
document.getElementById('play-icon')!.classList.add('hidden');
|
||||||
|
} else {
|
||||||
|
document.getElementById('pause-icon')!.classList.add('hidden');
|
||||||
|
document.getElementById('play-icon')!.classList.remove('hidden');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (button === "stop") {
|
||||||
|
this.isPlaying == false;
|
||||||
|
document.getElementById('play-label')!.textContent = "Play";
|
||||||
|
document.getElementById('pause-icon')!.classList.add('hidden');
|
||||||
|
document.getElementById('play-icon')!.classList.remove('hidden');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
this.flashBackground("#2d313d", 200);
|
this.flashBackground("#2d313d", 200);
|
||||||
const possible_selectors = [
|
const possible_selectors = [
|
||||||
'[id^="play-button-"]',
|
'[id^="play-button-"]',
|
||||||
'[id^="pause-button-"]',
|
|
||||||
'[id^="clear-button-"]',
|
'[id^="clear-button-"]',
|
||||||
'[id^="stop-button-"]',
|
'[id^="stop-button-"]',
|
||||||
];
|
];
|
||||||
@ -791,7 +834,6 @@ export class Editor {
|
|||||||
document
|
document
|
||||||
.querySelectorAll(possible_selectors[selector])
|
.querySelectorAll(possible_selectors[selector])
|
||||||
.forEach((button) => {
|
.forEach((button) => {
|
||||||
if (highlight) button.children[0].classList.add("fill-orange-300");
|
|
||||||
if (highlight) button.children[0].classList.add("animate-pulse");
|
if (highlight) button.children[0].classList.add("animate-pulse");
|
||||||
});
|
});
|
||||||
// All other buttons must lose the highlighting
|
// All other buttons must lose the highlighting
|
||||||
@ -800,10 +842,8 @@ export class Editor {
|
|||||||
possible_selectors.filter((_, index) => index != selector).join(",")
|
possible_selectors.filter((_, index) => index != selector).join(",")
|
||||||
)
|
)
|
||||||
.forEach((button) => {
|
.forEach((button) => {
|
||||||
button.children[0].classList.remove("fill-orange-300");
|
|
||||||
button.children[0].classList.remove("text-orange-300");
|
|
||||||
button.children[0].classList.remove("bg-orange-300");
|
|
||||||
button.children[0].classList.remove("animate-pulse");
|
button.children[0].classList.remove("animate-pulse");
|
||||||
|
button.children[1].classList.remove("animate-pulse");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -900,7 +940,6 @@ export class Editor {
|
|||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
document.getElementById("buffer-search")!.value = "";
|
document.getElementById("buffer-search")!.value = "";
|
||||||
document.getElementById("editor")!.classList.remove("invisible");
|
document.getElementById("editor")!.classList.remove("invisible");
|
||||||
document.getElementById("modal")!.classList.add("invisible");
|
|
||||||
document.getElementById("modal-buffers")!.classList.add("invisible");
|
document.getElementById("modal-buffers")!.classList.add("invisible");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -931,45 +970,7 @@ export class Editor {
|
|||||||
// Creating the application
|
// Creating the application
|
||||||
const app = new Editor();
|
const app = new Editor();
|
||||||
|
|
||||||
// Starting the clock after displaying a modal
|
|
||||||
function startClock() {
|
|
||||||
document.getElementById("editor")!.classList.remove("invisible");
|
|
||||||
document.getElementById("modal")!.classList.add("hidden");
|
|
||||||
document
|
|
||||||
.getElementById("modal-container")!
|
|
||||||
.classList.remove("motion-safe:animate-pulse");
|
|
||||||
document
|
|
||||||
.getElementById("start-button")!
|
|
||||||
.removeEventListener("click", startClock);
|
|
||||||
document.removeEventListener("click", startClock);
|
|
||||||
document.removeEventListener("keydown", startOnEnter);
|
|
||||||
document.removeEventListener("click", startOnClick);
|
|
||||||
app.clock.start();
|
|
||||||
app.view.focus();
|
|
||||||
app.setButtonHighlighting("play", true);
|
|
||||||
}
|
|
||||||
|
|
||||||
function startOnEnter(e: KeyboardEvent) {
|
|
||||||
if (e.code === "Enter" || e.code === "Space") startClock();
|
|
||||||
}
|
|
||||||
|
|
||||||
function startOnClick(e: MouseEvent) {
|
|
||||||
if (e.button === 0) startClock();
|
|
||||||
}
|
|
||||||
|
|
||||||
document.addEventListener("keydown", startOnEnter);
|
|
||||||
document.addEventListener("click", startOnClick);
|
|
||||||
// document.getElementById("start-button")!.addEventListener("click", startClock);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param event The mouse event
|
|
||||||
*/
|
|
||||||
function reportMouseCoordinates(event: MouseEvent) {
|
|
||||||
app._mouseX = event.clientX;
|
|
||||||
app._mouseY = event.clientY;
|
|
||||||
}
|
|
||||||
|
|
||||||
window.addEventListener("mousemove", reportMouseCoordinates);
|
|
||||||
|
|
||||||
// When the user leaves the page, all the universes should be saved in the localStorage
|
// When the user leaves the page, all the universes should be saved in the localStorage
|
||||||
window.addEventListener("beforeunload", () => {
|
window.addEventListener("beforeunload", () => {
|
||||||
@ -982,3 +983,11 @@ window.addEventListener("beforeunload", () => {
|
|||||||
app.clock.stop();
|
app.clock.stop();
|
||||||
return null;
|
return null;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// function reportMouseCoordinates(event: MouseEvent) {
|
||||||
|
// app._mouseX = event.clientX;
|
||||||
|
// app._mouseY = event.clientY;
|
||||||
|
// }
|
||||||
|
|
||||||
|
onmousemove = function(e){console.log("mouse location:", e.clientX, e.clientY)}
|
||||||
|
|
||||||
|
|||||||
@ -1,3 +1,10 @@
|
|||||||
@tailwind base;
|
@tailwind base;
|
||||||
@tailwind components;
|
@tailwind components;
|
||||||
@tailwind utilities;
|
@tailwind utilities;
|
||||||
|
|
||||||
|
@layer utilities {
|
||||||
|
.striped .col-span-3, .striped .col-span-2 {
|
||||||
|
@apply bg-neutral-300
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
@ -1,22 +1,10 @@
|
|||||||
const global_text =`
|
const global_text =`
|
||||||
// Global buffer: a central buffer to command them all.
|
|
||||||
// ====================================================
|
|
||||||
// The global buffer is a special buffer used to control
|
|
||||||
// the general behavior of your universe. It is meant to
|
|
||||||
// be used as a "control room" for your universe. You can
|
|
||||||
// make use of several commands to control the execution
|
|
||||||
// flow of all the files:
|
|
||||||
// - script(universe/universes: number): run script(s)
|
|
||||||
`
|
`
|
||||||
|
|
||||||
const local_buffer =`
|
const local_buffer =`
|
||||||
// Local buffer: nine buffers to write your algorithms.
|
|
||||||
`
|
`
|
||||||
|
|
||||||
const init_buffer=`
|
const init_buffer=`
|
||||||
// Init buffer: a buffer to initialize the universe.
|
|
||||||
// This universe is runned once when the universe is
|
|
||||||
// loaded!
|
|
||||||
`
|
`
|
||||||
|
|
||||||
const note_buffer='// Notes buffer: a buffer to write your notes.'
|
const note_buffer='// Notes buffer: a buffer to write your notes.'
|
||||||
@ -36,4 +24,4 @@ export const tutorial_universe = {
|
|||||||
},
|
},
|
||||||
init: { candidate: init_buffer, committed: init_buffer, evaluations: 0 },
|
init: { candidate: init_buffer, committed: init_buffer, evaluations: 0 },
|
||||||
notes: { candidate: note_buffer },
|
notes: { candidate: note_buffer },
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,20 +1,16 @@
|
|||||||
|
|
||||||
|
|
||||||
/** @type {import('tailwindcss').Config} */
|
/** @type {import('tailwindcss').Config} */
|
||||||
export default {
|
export default {
|
||||||
content: [
|
content: ["./index.html", "./src/**/*.{js,ts,jsx,tsx}"],
|
||||||
"./index.html",
|
safelist: [
|
||||||
"./src/**/*.{js,ts,jsx,tsx}",
|
{
|
||||||
|
pattern: /hljs+/,
|
||||||
|
},
|
||||||
],
|
],
|
||||||
safelist: [{
|
|
||||||
pattern: /hljs+/,
|
|
||||||
}],
|
|
||||||
theme: {
|
theme: {
|
||||||
extend: {},
|
extend: {},
|
||||||
hljs: {
|
hljs: {
|
||||||
theme: 'nord',
|
theme: "rainbow",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
plugins: [require('tailwind-highlightjs')],
|
plugins: [require("tailwind-highlightjs")],
|
||||||
}
|
};
|
||||||
|
|
||||||
|
|||||||
788
²
788
²
@ -1,788 +0,0 @@
|
|||||||
const key_shortcut = (shortcut: string): string => {
|
|
||||||
return `<kbd class="lg:px-2 lg:py-1.5 px-1 py-1 lg:text-sm text-xs font-semibold text-gray-800 bg-gray-100 border border-gray-200 rounded-lg dark:bg-gray-600 dark:text-gray-100 dark:border-gray-500">${shortcut}</kbd>`;
|
|
||||||
};
|
|
||||||
|
|
||||||
const injectAvailableSamples = (): string => {
|
|
||||||
return "";
|
|
||||||
};
|
|
||||||
|
|
||||||
const introduction: string = `
|
|
||||||
# Welcome
|
|
||||||
|
|
||||||
Welcome to the Topos documentation. These pages are offering you an introduction to the software and to the ideas behind it. You can jump here anytime by pressing ${key_shortcut(
|
|
||||||
"Ctrl + D"
|
|
||||||
)}. Press again to make the documentation disappear. All your contributions are welcome!
|
|
||||||
|
|
||||||
## What is Topos?
|
|
||||||
|
|
||||||
Topos is an _algorithmic_ sequencer. Topos uses small algorithms to represent musical sequences and processes. These can be written in just a few lines of code. Topos is made to be _live-coded_. The _live coder_ strives for the constant interaction with algorithms and sound during a musical performance. Topos is aiming to be a digital playground for live algorithmic music.
|
|
||||||
|
|
||||||
|
|
||||||
Topos is deeply inspired by the [Monome Teletype](https://monome.org/). The Teletype is/was an open source hardware module for Eurorack synthesizers. While the Teletype was initially born as an hardware module, Topos aims to be a web-browser based software sequencer from the same family! It is a sequencer, a scriptable interface, a companion for algorithmic music-making. Topos wishes to fullfill the same goal than the Teletype, keeping the same spirit alive on the web. It is free, open-source, and made to be shared and used by everyone.
|
|
||||||
|
|
||||||
## Example
|
|
||||||
|
|
||||||
Press ${key_shortcut(
|
|
||||||
"Ctrl + G"
|
|
||||||
)} to switch to the global file. This is where everything starts! Evaluate the following script there by pasting and pressing ${key_shortcut(
|
|
||||||
"Ctrl + Enter"
|
|
||||||
)}. You are now making music:
|
|
||||||
|
|
||||||
<pre><code class="language-javascript">
|
|
||||||
bpm(80)
|
|
||||||
mod(0.25) :: sound('sawtooth')
|
|
||||||
.note(seqbar(
|
|
||||||
pick(60, 67, 63) - 12, pick(60, 67, 63) - 12,
|
|
||||||
pick(60, 67, 63) - 12 + 5, pick(60, 67, 63) - 12 + 5,
|
|
||||||
pick(60, 67, 63) - 12 + 7, pick(60, 67, 63) - 12 + 7) + (sometimes() ? 24 : 12))
|
|
||||||
.sustain(0.1).fmi(8).fmh(4).room(0.9)
|
|
||||||
.gain(0.75).cutoff(500 + usine(8) * 10000)
|
|
||||||
.delay(0.5).delaytime(bpm() / 60 / 4 / 3)
|
|
||||||
.delayfeedback(0.25)
|
|
||||||
.out()
|
|
||||||
mod(1) && snd('kick').out()
|
|
||||||
mod(2) && snd('snare').out()
|
|
||||||
mod(.5) && snd('hat').out()
|
|
||||||
</code></pre>
|
|
||||||
`;
|
|
||||||
|
|
||||||
const software_interface: string = `
|
|
||||||
# Interface
|
|
||||||
|
|
||||||
The Topos interface is entirely dedicated to highlight the core concepts at play: _scripts_ and _universes_. By understanding the interface, you will already understand quite a lot about Topos and how to play music with it. Make sure to learn the dedicated keybindings as well and you will fly!
|
|
||||||
|
|
||||||
## Scripts
|
|
||||||
|
|
||||||
Every Topos session is composed of several small scripts. A set of scripts is called a _universe_. Every script is written using the JavaScript programming language and describes a musical or algorithmic process that takes place over time.
|
|
||||||
|
|
||||||
- **the global script** (${key_shortcut(
|
|
||||||
"Ctrl + G"
|
|
||||||
)}): _Evaluated for every clock pulse_. The central piece, acting as the conductor for all the other scripts. You can also jam directly from the global script to test your ideas before pushing them to a separate script. You can also access that script using the ${key_shortcut(
|
|
||||||
"F10"
|
|
||||||
)} key.
|
|
||||||
- **the local scripts** (${key_shortcut(
|
|
||||||
"Ctrl + L"
|
|
||||||
)}): _Evaluated on demand_. Local scripts are used to store anything too complex to sit in the global script. It can be a musical process, a whole section of your composition, a complex controller that you've built for your hardware, etc... You can also switch to one of the local scripts by using the function keys (${key_shortcut(
|
|
||||||
"F1"
|
|
||||||
)} to ${key_shortcut("F9")}).
|
|
||||||
- **the init script** (${key_shortcut(
|
|
||||||
"Ctrl + I"
|
|
||||||
)}): _Evaluated on program load_. Used to set up the software the session to the desired state before playing (_bpm_, etc...). You can also access that script using the ${key_shortcut(
|
|
||||||
"F11"
|
|
||||||
)} key.
|
|
||||||
- **the note file** (${key_shortcut(
|
|
||||||
"Ctrl + N"
|
|
||||||
)}): _Not evaluated_. Used to store your thoughts or commentaries about the session you are currently playing. It is nothing more than a scratchpad really!
|
|
||||||
|
|
||||||
## Universes
|
|
||||||
|
|
||||||
A set of files is called a _universe_. Topos can store several universes and switch immediately from one to another. You can switch between universes by pressing ${key_shortcut(
|
|
||||||
"Ctrl + B"
|
|
||||||
)}. You can also create a new universe by entering a name that has never been used before. _Universes_ are only referenced by their names. Once a universe is loaded, it is not possible to call any data/code from any other universe.
|
|
||||||
|
|
||||||
Switching between universes will not stop the transport nor reset the clock. You are switching the context but time keeps flowing. This can be useful to prepare immediate transitions between songs and parts. Think of universes as an algorithmic set of music. All scripts in a given universe are aware about how many times they have been runned already. You can reset that value programatically.
|
|
||||||
|
|
||||||
You can clear the current universe by pressing the flame button on the top right corner of the interface. This will clear all the scripts and the note file. **Note:** there is no shortcut for clearing a universe. We do not want to loose your work by mistake!
|
|
||||||
|
|
||||||
# Sharing your work
|
|
||||||
|
|
||||||
Click on the Topos logo in the top bar. Your URL will change to something much longer and complex. The same URL will be copied to your clipboard. Send this link to your friends to share the universe you are currently working on with them.
|
|
||||||
|
|
||||||
- The imported universe will always get a randomly generated name such as: <icode>random_silly_llama</icode>.
|
|
||||||
- Topos will automatically fetch and switch to the universe that was sent to you. Your previous universe is still accessible if you switch to it, don't worry!
|
|
||||||
`;
|
|
||||||
|
|
||||||
const time: string = `
|
|
||||||
# Time
|
|
||||||
|
|
||||||
Time in Topos is flowing just like on a drum machine. Topos is counting bars, beats and pulses. The time can be **paused**, **resumed** and/or **resetted**. Pulses are flowing at a given **BPM** (_beats per minute_). There are three core values that you will often interact with in one form or another:
|
|
||||||
|
|
||||||
- **bars**: how many bars have elapsed since the origin of time.
|
|
||||||
- **beats**: how many beats have elapsed since the beginning of the bar.
|
|
||||||
- **pulse**: how many pulses have elapsed since the last beat.
|
|
||||||
|
|
||||||
To change the tempo, use the <icode>bpm(number)</icode> function. The transport is controlled by the interface buttons, by the keyboard shortcuts or using the <icode>play()</icode>, <icode>pause()</icode> and <icode>stop()</icode> functions. You will soon learn how to manipulate time to your liking for backtracking, jumping forward, etc... The traditional timeline model has little value when you can script everything.
|
|
||||||
|
|
||||||
**Note:** the <icode>bpm(number)</icode> function can serve both for getting and setting the **BPM** value.
|
|
||||||
|
|
||||||
## Simple rhythms
|
|
||||||
|
|
||||||
Let's study two very simple rhythmic functions, <icode>mod(n: ...number[])</icode> and <icode>onbeat(...n:number[])</icode>. They are both easy to understand and powerful enough to get you to play your first rhythms.
|
|
||||||
|
|
||||||
- <icode>mod(...n: number[])</icode>: this function will return true every _n_ pulsations. The value <icode>1</icode> will return <icode>true</icode> at the beginning of each beat. Floating point numbers like <icode>0.5</icode> or <icode>0.25</icode> are also accepted. Multiple values can be passed to <icode>mod</icode> to generate more complex rhythms.
|
|
||||||
|
|
||||||
|
|
||||||
\`\`\`javascript
|
|
||||||
mod(1) :: sound('kick').out() // A kickdrum played every beat
|
|
||||||
mod(.5) :: sound('kick').out() // A kickdrum played twice per beat
|
|
||||||
mod(.25) :: sound('kick').out() // A kickdrum played four times every beat
|
|
||||||
mod(1/3) :: sound('kick').out() // A funnier ratio!
|
|
||||||
mod(1, 2.5)::sound('hh').out() // A great high-hat pattern
|
|
||||||
mod(1,3.25,2.5)::snd('hh').out() // A somewhat weirder pattern
|
|
||||||
\`\`\`
|
|
||||||
|
|
||||||
- <icode>onbeat(...n: number[])</icode>: By default, the bar is set in <icode>4/4</icode> with four beats per bar. The <icode>onbeat</icode> function allows you to lock on to a specific beat to execute some code. It can accept multiple arguments. It's usage is very straightforward and not hard to understand. You can pass integers or floating point numbers.
|
|
||||||
|
|
||||||
\`\`\`javascript
|
|
||||||
onbeat(1,2,3,4)::snd('kick').out() // Bassdrum on each beat
|
|
||||||
onbeat(2,4)::snd('snare').out() // Snare on acccentuated beats
|
|
||||||
onbeat(1.5,2.5,3.5, 3.75)::snd('hat').out() // Cool high-hats
|
|
||||||
\`\`\`
|
|
||||||
|
|
||||||
## Rhythm generators
|
|
||||||
|
|
||||||
We included a bunch of popular rhythm generators in Topos such as the euclidian rhythms algorithms or the one to generate rhythms based on a binary sequence. They all work using _iterators_ that you will gradually learn to use for iterating over lists. Note that they are levaraging <icode>mod(...n:number[])</icode> that you just learned about!
|
|
||||||
|
|
||||||
- <icode>euclid(iterator: number, pulses: number, length: number, rotate: number): boolean</icode>: generates <icode>true</icode> or <icode>false</icode> values from an euclidian rhythm sequence. This algorithm is very popular in the electronic music making world.
|
|
||||||
|
|
||||||
\`\`\`javascript
|
|
||||||
mod(.5) && euclid($(1), 5, 8) && snd('kick').out()
|
|
||||||
mod(.5) && euclid($(2), 2, 8) && snd('sd').out()
|
|
||||||
\`\`\`
|
|
||||||
|
|
||||||
- <icode>bin(iterator: number, n: number): boolean</icode>: a binary rhythm generator. It transforms the given number into its binary representation (_e.g_ <icode>34</icode> becomes <icode>100010</icode>). It then returns a boolean value based on the iterator in order to generate a rhythm.
|
|
||||||
|
|
||||||
\`\`\`javascript
|
|
||||||
mod(.5) && bin($(1), 34) && snd('kick').out()
|
|
||||||
mod(.5) && bin($(2), 48) && snd('sd').out()
|
|
||||||
\`\`\`
|
|
||||||
|
|
||||||
If you don't find it spicy enough, you can add some more probabilities to your rhythms by taking advantage of the probability functions. See the functions documentation page to learn more about them.
|
|
||||||
|
|
||||||
\`\`\`javascript
|
|
||||||
prob(60)::mod(.5) && euclid($(1), 5, 8) && snd('kick').out()
|
|
||||||
prob(60)::mod(.5) && euclid($(2), 3, 8) && snd('sd').out()
|
|
||||||
prob(80)::mod(.5) && sound('hh').out()
|
|
||||||
\`\`\`
|
|
||||||
|
|
||||||
## Larger time divisions
|
|
||||||
|
|
||||||
Now you know how to play some basic rhythmic music but you are a bit stuck in a one-bar long loop. Let's see how we can think about time flowing on longer periods. The functions you are going to learn now are _very fundamental_ and all the fun comes from mastering them. **Read and experiment a lot with the following examples**.
|
|
||||||
|
|
||||||
- <icode>div(n: number)</icode>: the <icode>div</icode> is a temporal switch. If the value <icode>2</icode> is given, the function will return <icode>true</icode> for two beats and <icode>false</icode> for two beats. There are multiple ways to use it effectively. You can pass an integer or a floating point number. Here are some examples.
|
|
||||||
|
|
||||||
\`\`\`javascript
|
|
||||||
mod(1)::snd('kick').out(); // Playing on every beat
|
|
||||||
div(2)::mod(.75)::snd('hat').out(); // Playing only every two beats
|
|
||||||
\`\`\`
|
|
||||||
|
|
||||||
You can also use it to think about **longer durations** spanning over multiple bars.
|
|
||||||
|
|
||||||
\`\`\`javascript
|
|
||||||
// Rap God VS Lil Wild -- Adel Faure
|
|
||||||
if (div(16)) {
|
|
||||||
// Playing this part for two bars
|
|
||||||
mod(1.5)::snd('kick').out()
|
|
||||||
mod(2)::snd('snare').out()
|
|
||||||
mod(.5)::snd('hh').out()
|
|
||||||
} else {
|
|
||||||
// Now adding some birds and tablas
|
|
||||||
mod(1.5)::snd('kick').out()
|
|
||||||
mod(2)::snd('snare').out()
|
|
||||||
mod(.5)::snd('hh').out()
|
|
||||||
mod(.5)::snd('tabla').speed(pick(1,2)).end(0.5).out()
|
|
||||||
mod(2.34)::snd('birds').n(irand(1,10))
|
|
||||||
.delay(0.5).delaytime(0.5).delayfb(0.25).out()
|
|
||||||
mod(.5)::snd('diphone').end(0.5).n(pick(1,2,3,4)).out()
|
|
||||||
}
|
|
||||||
\`\`\`
|
|
||||||
|
|
||||||
And you can use it for other things inside a method parameter:
|
|
||||||
|
|
||||||
\`\`\`javascript
|
|
||||||
mod(.5)::snd(div(2) ? 'kick' : 'hat').out()
|
|
||||||
\`\`\`
|
|
||||||
|
|
||||||
- <icode>divbar(n: number)</icode>: works just like <icode>div</icode> but at the level of bars instead of beats. It allows you to think about even bigger time cycles. You can also pair it with regular <icode>div</icode> for making complex algorithmic beats.
|
|
||||||
|
|
||||||
\`\`\`javascript
|
|
||||||
divbar(2)::mod(1)::snd('kick').out()
|
|
||||||
divbar(3)::mod(.5)::snd('hat').out()
|
|
||||||
\`\`\`
|
|
||||||
|
|
||||||
- <icode>onbar(n: number, ...bar: number[])</icode>: The first argument, <icode>n</icode>, is used to divide the time in a period of <icode>n</icode> consecutive bars. The following arguments are bar numbers to play on. For example, <icode>onbar(5, 1, 4)</icode> will return <icode>true</icode> on bar <icode>1</icode> and <icode>4</icode> but return <icode>false</icode> the rest of the time. You can easily divide time that way.
|
|
||||||
|
|
||||||
|
|
||||||
\`\`\`javascript
|
|
||||||
// Only play on the fourth bar of a four bar cycle.
|
|
||||||
onbar(4, 4)::mod(.5)::snd('hh').out();
|
|
||||||
|
|
||||||
// Here comes a longer version using JavaScript normal control flow
|
|
||||||
if (onbar(4, 1, 3)) {
|
|
||||||
mod(1)::snd('kick').out();
|
|
||||||
} else {
|
|
||||||
mod(.5)::snd('sd').out();
|
|
||||||
}
|
|
||||||
\`\`\`
|
|
||||||
|
|
||||||
## What are pulses?
|
|
||||||
|
|
||||||
To make a beat, you need a certain number of time grains or **pulses**. The **pulse** is also known as the [PPQN](https://en.wikipedia.org/wiki/Pulses_per_quarter_note). By default, Topos is using a _pulses per quarter note_ of 48. You can change it by using the <icode>ppqn(number)</icode> function. It means that the lowest possible rhythmic value is 1/48 of a quarter note. That's plenty of time already.
|
|
||||||
|
|
||||||
**Note:** the <icode>ppqn(number)</icode> function can serve both for getting and setting the **PPQN** value.
|
|
||||||
|
|
||||||
## Time Primitives
|
|
||||||
|
|
||||||
Every script can access the current time by using the following functions:
|
|
||||||
|
|
||||||
- <icode>bar(n: number)</icode>: returns the current bar since the origin of time.
|
|
||||||
|
|
||||||
- <icode>beat(n: number)</icode>: returns the current beat since the beginning of the bar.
|
|
||||||
|
|
||||||
- <icode>ebeat()</icode>: returns the current beat since the origin of time (counting from 1).
|
|
||||||
|
|
||||||
- <icode>pulse()</icode>: returns the current bar since the origin of the beat.
|
|
||||||
|
|
||||||
- <icode>ppqn()</icode>: returns the current **PPQN** (see above).
|
|
||||||
|
|
||||||
- <icode>bpm()</icode>: returns the current **BPM** (see above).
|
|
||||||
|
|
||||||
- <icode>time()</icode>: returns the current wall clock time, the real time of the system.
|
|
||||||
|
|
||||||
These values are **extremely useful** to craft more complex syntax or to write musical scores. However, Topos is also offering more high-level sequencing functions to make it easier to play music. You can use the time functions as conditionals. The following example will play a pattern A for 2 bars and a pattern B for 2 bars:
|
|
||||||
|
|
||||||
\`\`\`javascript
|
|
||||||
if((bar() % 4) > 1) {
|
|
||||||
mod(1) && sound('kick').out()
|
|
||||||
rarely() && mod(.5) && sound('sd').out()
|
|
||||||
mod(.5) && sound('jvbass').freq(500).out()
|
|
||||||
} else {
|
|
||||||
mod(.5) && sound('hh').out()
|
|
||||||
mod(.75) && sound('cp').out()
|
|
||||||
mod(.5) && sound('jvbass').freq(250).out()
|
|
||||||
}
|
|
||||||
\`\`\`
|
|
||||||
`;
|
|
||||||
|
|
||||||
const midi: string = `
|
|
||||||
# MIDI
|
|
||||||
|
|
||||||
You can use Topos to play MIDI thanks to the [WebMIDI API](https://developer.mozilla.org/en-US/docs/Web/API/Web_MIDI_API). You can currently send notes, control change, program change and so on. You can also send a MIDI Clock to your MIDI devices or favorite DAW. Note that Topos is also capable of playing MIDI using **Ziffers** which provides a better syntax for melodic expression.
|
|
||||||
|
|
||||||
## Notes
|
|
||||||
- <icode>midi(note: number|object)</icode>: send a MIDI Note. Object can take parameters {note: number, channel: number, port: number|string, velocity: number}.
|
|
||||||
|
|
||||||
\`\`\`javascript
|
|
||||||
bpm(80) // Setting a default BPM
|
|
||||||
mod(.5) && midi(36 + seqbeat(0,12)).sustain(0.02).out()
|
|
||||||
mod(.25) && midi(pick(64, 76)).sustain(0.05).out()
|
|
||||||
mod(.75) && midi(seqbeat(64, 67, 69)).sustain(0.05).out()
|
|
||||||
sometimes() && mod(.25) && midi(seqbeat(64, 67, 69) + 24).sustain(0.05).out()
|
|
||||||
\`\`\`
|
|
||||||
|
|
||||||
## Note chaining
|
|
||||||
|
|
||||||
The <icode>midi(number|object)</icode> function can be chained to _specify_ a midi note more. For instance, you can add a duration, a velocity, a channel, etc... by chaining:
|
|
||||||
|
|
||||||
\`\`\`javascript
|
|
||||||
mod(0.25) && midi(60)
|
|
||||||
.sometimes(n=>n.note(irand(40,60)))
|
|
||||||
.sustain(0.05)
|
|
||||||
.channel(2)
|
|
||||||
.port("bespoke")
|
|
||||||
.out()
|
|
||||||
\`\`\`
|
|
||||||
|
|
||||||
## Control and Program Changes
|
|
||||||
|
|
||||||
- <icode>control_change({control: number, value: number, channel: number})</icode>: send a MIDI Control Change. This function takes a single object argument to specify the control message (_e.g._ <icode>control_change({control: 1, value: 127, channel: 1})</icode>).
|
|
||||||
|
|
||||||
\`\`\`javascript
|
|
||||||
control_change({control: pick(24,25), value: rI(1,120), channel: 1}))})
|
|
||||||
control_change({control: pick(30,35), value: rI(1,120) / 2, channel: 1}))})
|
|
||||||
\`\`\`
|
|
||||||
|
|
||||||
|
|
||||||
- <icode>program_change(program: number, channel: number)</icode>: send a MIDI Program Change. This function takes two arguments to specify the program and the channel (_e.g._ <icode>program_change(1, 1)</icode>).
|
|
||||||
|
|
||||||
\`\`\`javascript
|
|
||||||
program_change(pick(1,2,3,4,5,6,7,8), 1)
|
|
||||||
\`\`\`
|
|
||||||
|
|
||||||
|
|
||||||
## System Exclusive Messages
|
|
||||||
|
|
||||||
- <icode>sysex(...number[])</icode>: send a MIDI System Exclusive message. This function takes any number of arguments to specify the message (_e.g._ <icode>sysex(0x90, 0x40, 0x7f)</icode>).
|
|
||||||
|
|
||||||
## Clock
|
|
||||||
|
|
||||||
- <icode>midi_clock()</icode>: send a MIDI Clock message. This function is used to synchronize Topos with other MIDI devices or DAWs.
|
|
||||||
|
|
||||||
\`\`\`javascript
|
|
||||||
mod(.25) && midi_clock() // Sending clock to MIDI device from the global buffer
|
|
||||||
\`\`\`
|
|
||||||
|
|
||||||
## MIDI Output Selection
|
|
||||||
|
|
||||||
- <icode>midi_outputs()</icode>: Prints a list of available MIDI outputs. You can then use any output name to select the MIDI output you wish to use. **Note:** this function will print to the console. You can open the console by pressing ${key_shortcut(
|
|
||||||
"Ctrl + Shift + I"
|
|
||||||
)} in many web browsers.
|
|
||||||
- <icode>midi_output(output_name: string)</icode>: Selects the MIDI output to use. You can use the <icode>midi_outputs()</icode> function to get a list of available MIDI outputs first. If the MIDI output is not available, the function will do nothing and keep on with the currently selected MIDI Port.
|
|
||||||
|
|
||||||
`;
|
|
||||||
|
|
||||||
const sound: string = `
|
|
||||||
# Audio engine
|
|
||||||
|
|
||||||
The Topos audio engine is based on the [SuperDough](https://www.npmjs.com/package/superdough) audio backend, leveraging the [Web Audio API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Audio_API). The engine is capable of playing multiple samples, synths and effects at once. It is a very powerful and almost limitless tool to create complex sounds and textures. A set of default sounds are already provided but you can also load your own audio samples and synths!
|
|
||||||
|
|
||||||
## Sound basics
|
|
||||||
|
|
||||||
The basic function to play a sound is... <icode>sound(name: string)</icode> (you can also write <icode>snd</icode> to save some precious time). If the given sound or synthesizer exists in the database, it will be automatically queried/started and will start playing. Evaluate the following script in the global window:
|
|
||||||
|
|
||||||
\`\`\`javascript
|
|
||||||
mod(1) && sound('bd').out()
|
|
||||||
mod(0.5) && sound('hh').out()
|
|
||||||
\`\`\`
|
|
||||||
|
|
||||||
In plain english, this translates to:
|
|
||||||
|
|
||||||
> Every 48 pulses, play a kick drum.
|
|
||||||
> Every 24 pulses, play a high-hat.
|
|
||||||
|
|
||||||
Let's make it slightly more complex:
|
|
||||||
|
|
||||||
\`\`\`javascript
|
|
||||||
mod(1) && sound('bd').coarse(0.25).out()
|
|
||||||
mod(0.5) && sound('hh').delay(0.25).delaytime(0.125).out()
|
|
||||||
\`\`\`
|
|
||||||
|
|
||||||
Now, it reads as follow:
|
|
||||||
|
|
||||||
> Every 48 pulses, play a kick drum with some amount of distortion.
|
|
||||||
> Every 24 pulses, play a high-hat with 25% of the sound injected in
|
|
||||||
> a delay unit, with a delay time of 0.125 seconds.
|
|
||||||
|
|
||||||
Let's pause for a moment to explain what we just wrote. There are many things to be said:
|
|
||||||
- If you remove the **mod** instruction, you will end up with a deluge of kick drums and high-hats. The **mod** instruction is used to filter out pulses. It is a very useful instruction to create basic rhythms. Check out the **Time** page if you haven't read it already.
|
|
||||||
- Playing a sound always ends up with the <icode>.out()</icode> method that gives the instruction to send a message to the audio engine.
|
|
||||||
- Sounds are **composed** by adding qualifiers that will modify the sound or synthesizer being played (_e.g_ <icode>sound('...').blabla(...)..something(...).out()</icode>.
|
|
||||||
|
|
||||||
## Audio Sample Folders / Sample Files
|
|
||||||
|
|
||||||
When you type <icode>kick</icode> in the <icode>sound('kick').out()</icode> expression, you are referring to a sample folder containing multiple audio samples. If you look at the sample folder, it would look something like this:
|
|
||||||
|
|
||||||
\`\`\`shell
|
|
||||||
.
|
|
||||||
├── KICK9.wav
|
|
||||||
├── kick1.wav
|
|
||||||
├── kick10.wav
|
|
||||||
├── kick2-1.wav
|
|
||||||
├── kick2.wav
|
|
||||||
├── kick3-1.wav
|
|
||||||
├── kick3.wav
|
|
||||||
├── kick4.wav
|
|
||||||
├── kick5.wav
|
|
||||||
├── kick6.wav
|
|
||||||
├── kick7.wav
|
|
||||||
└── kick8.wav
|
|
||||||
\`\`\`
|
|
||||||
|
|
||||||
The <icode>.n(number)</icode> method can be used to pick a sample from the currently selected sample folder. For instance, the following script will play a random sample from the _kick_ folder:
|
|
||||||
|
|
||||||
\`\`\`javascript
|
|
||||||
mod(1) && sound('kick').n(pick(1,2,3,4,5,6,7,8)).out()
|
|
||||||
\`\`\`
|
|
||||||
|
|
||||||
Don't worry about the number. If it gets too big, it will be automatically wrapped to the number of samples in the folder. You can type any number, it will always fall on a sample. Let's use our mouse to select a sample number in a folder:
|
|
||||||
|
|
||||||
\`\`\`javascript
|
|
||||||
// Move your mouse to change the sample being used!
|
|
||||||
mod(.25) && sound('numbers').n(Math.floor(mouseX())).out()
|
|
||||||
\`\`\`
|
|
||||||
|
|
||||||
**Note:** the <icode>sound</icode> function can also be used to play synthesizers (see the **Synthesizers** page). In that case, the <icode>.n(n: number)</icode> becomes totally useless!
|
|
||||||
|
|
||||||
## Learning about sound modifiers
|
|
||||||
|
|
||||||
As we said earlier, the <icode>sound('sample_name')</icode> function can be chained to _specify_ a sound more. For instance, you can add a filter and some effects to your high-hat:
|
|
||||||
\`\`\`javascript
|
|
||||||
mod(0.5) && sound('hh')
|
|
||||||
.sometimes(s=>s.speed(pick(1,5,10)))
|
|
||||||
.room(0.5)
|
|
||||||
.cutoff(usine(2) * 5000)
|
|
||||||
.out()
|
|
||||||
\`\`\`
|
|
||||||
|
|
||||||
There are many possible arguments that you can add to your sounds. Learning them can take a long time but it will open up a lot of possibilities. Let's try to make it through all of them. They can all be used both with synthesizers and audio samples, which is kind of unconventional with normal / standard electronic music softwares.
|
|
||||||
|
|
||||||
## Amplitude
|
|
||||||
|
|
||||||
Simple controls over the amplitude (volume) of a given sound.
|
|
||||||
|
|
||||||
| Method | Alias | Description |
|
|
||||||
|---------|-------|------------------------------------------------------------|
|
|
||||||
| gain | | Volume of the synth/sample (exponential) |
|
|
||||||
| velocity| vel | Velocity (amplitude) from 0 to 1. Multipled with gain |
|
|
||||||
|
|
||||||
## Amplitude Enveloppe
|
|
||||||
|
|
||||||
**Superdough** is applying an **ADSR** envelope to every sound being played. This is a very standard and conventional amplitude envelope composed of four stages: _attack_, _decay_, _sustain_ and _release_. You will find the same parameters on most synthesizers.
|
|
||||||
|
|
||||||
| Method | Alias | Description |
|
|
||||||
|---------|-------|-----------------------------------------------|
|
|
||||||
| attack | atk | Attack value (time to maximum volume) |
|
|
||||||
| decay | dec | Decay value (time to decay to sustain level) |
|
|
||||||
| sustain | sus | Sustain value (gain when sound is held) |
|
|
||||||
| release | rel | Release value (time for the sound to die off) |
|
|
||||||
|
|
||||||
Note that the **sustain** value is not a duration but an amplitude value (how loud). The other values are the time for each stage to take place. Here is a fairly complete example using the <icode>sawtooth</icode> basic waveform.
|
|
||||||
|
|
||||||
\`\`\`javascript
|
|
||||||
mod(4)::sound('sawtooth').note(50).decay(0.5).sustain(0.5).release(2).out();
|
|
||||||
mod(2)::sound('sawtooth').note(50+7).decay(0.5).sustain(0.6).release(2).out();
|
|
||||||
mod(1)::sound('sawtooth').note(50+12).decay(0.5).sustain(0.7).release(2).out();
|
|
||||||
mod(.25)::sound('sawtooth').note(pick(50,57,62)+divseq(2, 12, 24, 0))
|
|
||||||
.cutoff(5000).sustain(0.5).release(0.1).out()
|
|
||||||
\`\`\`
|
|
||||||
|
|
||||||
## Sample Controls
|
|
||||||
|
|
||||||
There are some basic controls over the playback of each sample. This allows you to get into more serious sampling if you take the time to really work with your audio materials.
|
|
||||||
|
|
||||||
| Method | Alias | Description |
|
|
||||||
|---------|-------|--------------------------------------------------------|
|
|
||||||
| n | | Select a sample in the current folder (from <icode>0</icode> to infinity) |
|
|
||||||
| begin | | Beginning of the sample playback (between <icode>0</icode> and <icode>1</icode>) |
|
|
||||||
| end | | End of the sample (between <icode>0</icode> and <icode>1</icode>) |
|
|
||||||
| speed | | Playback speed (<icode>2</icode> = twice as fast) |
|
|
||||||
| cut | | Set with <icode>0</icode> or <icode>1</icode>. Will cut the sample as soon as another sample is played on the same bus |
|
|
||||||
| clip | | Multiply the duration of the sample with the given number |
|
|
||||||
|
|
||||||
## Filters
|
|
||||||
|
|
||||||
There are three basic filters: a _lowpass_, _highpass_ and _bandpass_ filters with rather soft slope. Each of them can take up to two arguments. You can also use only the _cutoff_ frequency and the resonance will stay to its default nominal value.
|
|
||||||
|
|
||||||
| Method | Alias | Description |
|
|
||||||
|------------|-------|-----------------------------------------|
|
|
||||||
| cutoff | lpf | Cutoff frequency of the lowpass filter |
|
|
||||||
| resonance | lpq | Resonance of the lowpass filter |
|
|
||||||
| hcutoff | hpf | Cutoff frequency of the highpass filter |
|
|
||||||
| hresonance | hpq | Resonance of the highpass filter |
|
|
||||||
| bandf | bpf | Cutoff frequency of the bandpass filter |
|
|
||||||
| bandq | bpq | Resonance of the bandpass filter |
|
|
||||||
| vowel | | Formant filter with (vocal quality) |
|
|
||||||
|
|
||||||
\`\`\`javascript
|
|
||||||
mod(.5) && snd('sawtooth')
|
|
||||||
.cutoff(pick(2000,500)) + usine(.5) * 4000)
|
|
||||||
.resonance(0.9).freq(pick(100,150))
|
|
||||||
.out()
|
|
||||||
\`\`\`
|
|
||||||
|
|
||||||
## Reverb
|
|
||||||
|
|
||||||
A basic reverberator that you can use to give some depth to your sounds. This simple reverb design has a _LoFI_ quality that can be quite useful on certain sounds.
|
|
||||||
|
|
||||||
| Method | Alias | Description |
|
|
||||||
|------------|-------|---------------------------------|
|
|
||||||
| room | | The more, the bigger the reverb (between <icode>0</icode> and <icode>1</icode>.|
|
|
||||||
| size | | Reverberation amount |
|
|
||||||
|
|
||||||
\`\`\`javascript
|
|
||||||
mod(2)::snd('cp').room(1).size(0.9).out()
|
|
||||||
\`\`\`
|
|
||||||
|
|
||||||
## Delay
|
|
||||||
|
|
||||||
|
|
||||||
| Method | Alias | Description |
|
|
||||||
|------------|-------|---------------------------------|
|
|
||||||
| delay | | Delay _wet/dry_ (between <icode>0</icode> and <icode>1</icode> |
|
|
||||||
| delaytime | delayt | |
|
|
||||||
| delayfeedback| delayfb | |
|
|
||||||
|
|
||||||
| Method | Description |
|
|
||||||
| -------------------------------------- | ----------- |
|
|
||||||
| <icode>unit(value: number)</icode> | Sets the unit value |
|
|
||||||
| <icode>frequency(value: number)</icode>| Sets the playback sample frequency |
|
|
||||||
| <icode>nudge(value: number)</icode> | Adjusts the start time of the sound by the given value |
|
|
||||||
| <icode>cut(value: number)</icode>| Cut the sample if it overlaps on the same orbit. |
|
|
||||||
| <icode>loop(value: number)</icode>| Loops the sample. |
|
|
||||||
| <icode>note(value: number)</icode>| Sets the note value of the sound. |
|
|
||||||
| <icode>coarse(value: number)</icode>| Adds some flavor of saturation. |
|
|
||||||
| <icode>crush(value: number)</icode>| Adds some amount of bitcrush on the given sound. |
|
|
||||||
| <icode>shape(value: number)</icode>| Adds some distortion. |
|
|
||||||
| <icode>pan(value: number)</icode>| Sets the panoramic value of the sound (in stereo, between <icode>0.0</icode> and <icode>1.0</icode>). |
|
|
||||||
| <icode>delay(value: number)</icode>| Sets the delay wet/dry value. |
|
|
||||||
| <icode>delayfeedback(value: number)</icode>| Sets delay feedback. |
|
|
||||||
| <icode>delaytime(value: number)</icode>| Sets delay time (in seconds). |
|
|
||||||
| <icode>orbit(value: number)</icode>| Sets the orbit value of the sound. |
|
|
||||||
`;
|
|
||||||
|
|
||||||
const samples: string = `
|
|
||||||
# Audio Samples
|
|
||||||
|
|
||||||
## Available audio samples
|
|
||||||
|
|
||||||
${injectAvailableSamples()}
|
|
||||||
|
|
||||||
`;
|
|
||||||
|
|
||||||
const synths: string = `
|
|
||||||
# Synthesizers
|
|
||||||
|
|
||||||
Topos comes with a small number of basic synthesizers. These synths are based on a basic [WebAudio](https://developer.mozilla.org/en-US/docs/Web/API/Web_Audio_API) design. For heavy synthesis duties, please use MIDI and speak to more complex instruments.
|
|
||||||
|
|
||||||
# Substractive Synthesis
|
|
||||||
|
|
||||||
The <icode>sound</icode> function can take the name of a synthesizer as first argument.
|
|
||||||
- <icode>sine</icode>, <icode>sawtooth</icode>,<icode>triangle</icode>, <icode>square</icode> for the waveform selection.
|
|
||||||
- <icode>cutoff</icode> and <icode>resonance</icode> for adding a low-pass filter with cutoff frequency and filter resonance.
|
|
||||||
- <icode>hcutoff</icode> or <icode>bandf</icode> to switch to a high-pass or bandpass filter.
|
|
||||||
- <icode>hresonance</icode> and <icode>bandq</icode> for the resonance parameter of these filters.
|
|
||||||
|
|
||||||
Here is a simple example of a substractive synth:
|
|
||||||
|
|
||||||
\`\`\`javascript
|
|
||||||
mod(.5) && snd('sawtooth')
|
|
||||||
.cutoff(pick(2000,500)) + usine(.5) * 4000)
|
|
||||||
.resonance(0.9).freq(pick(100,150))
|
|
||||||
.out()
|
|
||||||
\`\`\`
|
|
||||||
|
|
||||||
|
|
||||||
# Frequency Modulation Synthesis (FM)
|
|
||||||
|
|
||||||
The same basic waveforms can take additional methods to switch to a basic two operators FM synth design (with _carrier_ and _modulator_). FM Synthesis is a complex topic but take this advice: simple ratios will yield stable and harmonic sounds, complex ratios will generate noises, percussions and gritty sounds.
|
|
||||||
|
|
||||||
- <icode>fmi</icode> (_frequency modulation index_): a floating point value between <icode>1</icode> and <icode>n</icode>.
|
|
||||||
- <icode>fmh</icode> (_frequency modulation harmonic ratio_): a floating point value between <icode>1</icode> and <icode>n</icode>.
|
|
||||||
|
|
||||||
And here is a simple example:
|
|
||||||
|
|
||||||
\`\`\`javascript
|
|
||||||
mod(.25) && snd('sine')
|
|
||||||
.fmi(pick(1,2,4,8))
|
|
||||||
.fmh(divseq(2, 1,2,4,8))
|
|
||||||
.freq(pick(100,150))
|
|
||||||
.sustain(0.1)
|
|
||||||
.out()
|
|
||||||
\`\`\`
|
|
||||||
|
|
||||||
**Note::** you can also set the _modulation index_ and the _harmonic ratio_ with the <icode>fm</icode> argument. You will have to feed both as a string: <icode>fm('2:4')</icode>. If you only feed one number, only the _modulation index_ will be updated.
|
|
||||||
|
|
||||||
`;
|
|
||||||
|
|
||||||
const about: string = `
|
|
||||||
# About Topos
|
|
||||||
|
|
||||||
## The Topos Project
|
|
||||||
|
|
||||||
Topos is an experimental web based algorithmic sequencer programmed by **BuboBubo** ([Raphaël Forment](https://raphaelforment.fr) and **Amiika** ([Miika Alonen](https//github.com/amiika). It is written using [TypeScript](https://google.fr) and [Vite](https://google.fr). Many thanks to Felix Roos for making the [Superdough](https://www.npmjs.com/package/superdough) audio backend available for experimentation. This project is based on the [Monome Teletype](https://monome.org) by [Brian Crabtree](https://nnnnnnnn.co/) and [Kelli Cain](https://kellicain.com/). We hope to follow and honor the same spirit of sharing and experimentation. How much can the Teletype be extended while staying accessible and installation-free?
|
|
||||||
|
|
||||||
## About Live Coding
|
|
||||||
|
|
||||||
**Amiika** and I are both very involved in the [TOPLAP](https://toplap.org) and [Algorave](https://algorave.com) scenes. We previously worked on the [Sardine](https://sardine.raphaelforment.fr) live coding environment for Python. **Amiika** has been working hard on its own algorithmic pattern language called [Ziffers](https://github.com/amiika/ziffers). A version of it is available in Topos! **Raphaël** is doing live coding with other folks from the [Cookie Collective](https://cookie.paris) and from the city of Lyon (France).
|
|
||||||
|
|
||||||
## Free and open-source software
|
|
||||||
|
|
||||||
Topos is a free and open-source software distributed under [GPL-3.0](https://github.com/Bubobubobubobubo/Topos/blob/main/LICENSE) licence. We welcome all contributions and ideas. You can find the source code on [GitHub](https://github.com/Bubobubobubobubo/topos). You can also join us on [Discord](https://discord.gg/8Q2QV6Z6) to discuss about the project and live coding in general.
|
|
||||||
|
|
||||||
**Have fun!**
|
|
||||||
`;
|
|
||||||
|
|
||||||
const code: string = `
|
|
||||||
# Code
|
|
||||||
|
|
||||||
Topos is using the [JavaScript](https://en.wikipedia.org/wiki/JavaScript) syntax because it lives in a web browser where JS is the default programming language. It is also a language that you can learn to speak quite fast if you are already familiar with other programming languages. You are not going to write a lot of code anyway but familiarity with the language can help. Here are some good resources:
|
|
||||||
|
|
||||||
- [MDN (Mozilla Web Docs)](https://developer.mozilla.org/): it covers pretty much anything and is considered to be a reliable source to learn how the web currently works. We use it quite a lot to develop Topos.
|
|
||||||
|
|
||||||
- [Learn JS in Y Minutes](https://learnxinyminutes.com/docs/javascript/): a good tour of the language. Can be useful as a refresher.
|
|
||||||
|
|
||||||
- [The Modern JavaScript Tutorial](https://javascript.info/): another well known source to learn the language.
|
|
||||||
|
|
||||||
You **do not need to have any prior knowledge of programming** to use Topos. It can also be used as a **valuable resource** to learn some basic programming.
|
|
||||||
|
|
||||||
## How is the code evaluated?
|
|
||||||
|
|
||||||
The code you enter in any of the scripts is evaluated in strict mode. This tells your browser that the code you run can be optimized quite agressively. We need this because by default, **the global script is evaluated 48 times per beat**. It also means that you can crash at the speed of light :smile:. The local and initialisation scripts are evaluated on demand, one run at a time. There are some things to keep in mind:
|
|
||||||
|
|
||||||
- **about variables:** the state of your variables is not kept between iterations. If you write <icode>let a = 2</icode> and change the value later on, the value will be reset to <icode>2</icode> after each run! There are other ways to deal with variables and to share variables between scripts! Some variables like **iterators** can keep their state between iterations because they are saved **with the file itself**.
|
|
||||||
- **about errors and printing:** your code will crash! Don't worry, it will hopefully try to crash in the most gracious way possible. To check if your code is erroring, you will have to open the dev console with ${key_shortcut(
|
|
||||||
"Ctrl + Shift + I"
|
|
||||||
)}. You cannot directly use <icode>console.log('hello, world')</icode> in the interface. You will have to open the console as well to see your messages being printed there!
|
|
||||||
- **about new syntax:** sometimes, we have taken liberties with the JavaScript syntax in order to make it easier/faster to write on stage. <icode>&&</icode> can also be written <icode>::</icode> or <icode>-></icode> because it is faster to type or better for the eyes!
|
|
||||||
|
|
||||||
## About crashes and bugs
|
|
||||||
|
|
||||||
Things will crash, that's also part of the show. You will learn progressively to avoid mistakes and to write safer code. Do not hesitate to kill the page or to stop the transport if you feel overwhelmed by an algorithm blowing up. There are no safeties in place to save you. This is to ensure that you have all the available possible room to write bespoke code and experiment with your ideas through code.
|
|
||||||
`;
|
|
||||||
|
|
||||||
const functions: string = `
|
|
||||||
# Functions
|
|
||||||
|
|
||||||
## Global Shared Variables
|
|
||||||
|
|
||||||
By default, each script is independant from each other. Scripts live in their own bubble and you cannot get or set variables affecting a script from any other script. **However**, everybody knows that global variables are cool and should be used everywhere. This is an incredibely powerful tool to use for radically altering a composition in a few lines of code.
|
|
||||||
|
|
||||||
- <icode>variable(a: number | string, b?: any)</icode>: if only one argument is provided, the value of the variable will be returned through its name, denoted by the first argument. If a second argument is used, it will be saved as a global variable under the name of the first argument.
|
|
||||||
- <icode>delete_variable(name: string)</icode>: deletes a global variable from storage.
|
|
||||||
- <icode>clear_variables()</icode>: clear **ALL** variables. **This is a destructive operation**!
|
|
||||||
|
|
||||||
## Counter and iterators
|
|
||||||
|
|
||||||
You will often need to use iterators and/or counters to index over data structures (getting a note from a list of notes, etc...). There are functions ready to be used for this. Each script also comes with its own iterator that you can access using the <icode>i</icode> variable. **Note:** the script iteration count is **not** resetted between sessions. It will continue to increase the more you play, even if you just picked up an old project.
|
|
||||||
|
|
||||||
- <icode>counter(name: number | string, limit?: number, step?: number)</icode>: reads the value of the counter <icode>name</icode>. You can also call this function using the dollar symbol: <icode>$</icode>.
|
|
||||||
- <icode>limit?</icode>: counter upper limit before wrapping up.
|
|
||||||
- <icode>step?</icode>: incrementor. If step is <icode>2</icode>, the iterator will go: <icode>0, 2, 4, 6</icode>, etc...
|
|
||||||
|
|
||||||
- <icode>drunk(n?: number)</icode>: returns the value of the internal drunk walk counter. This iterator will sometimes go up, sometimes go down. It comes with companion functions that you can use to finetune its behavior.
|
|
||||||
- <icode>drunk_max(max: number)</icode>: sets the maximum value.
|
|
||||||
- <icode>drunk_min(min: number)</icode>: sets the minimum value.
|
|
||||||
- <icode>drunk_wrap(wrap: boolean)</icode>: whether to wrap the drunk walk to 0 once the upper limit is reached or not.
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## Scripts
|
|
||||||
|
|
||||||
You can control scripts programatically. This is the core concept of Topos after all!
|
|
||||||
|
|
||||||
- <icode>script(...number: number[])</icode>: call one or more scripts (_e.g. <icode>script(1,2,3,4)</icode>). Once called, scripts will be evaluated once. There are nine local scripts by default. You cannot call the global script nor the initialisation script.
|
|
||||||
|
|
||||||
- <icode>clear_script(number)</icode>: deletes the given script.
|
|
||||||
- <icode>copy_script(from: number, to: number)</icode>: copies a local script denoted by its number to another local script. **This is a destructive operation!**
|
|
||||||
|
|
||||||
## Mouse
|
|
||||||
|
|
||||||
You can get the current position of the mouse on the screen by using the following functions:
|
|
||||||
|
|
||||||
- <icode>mouseX()</icode>: the horizontal position of the mouse on the screen (as a floating point number).
|
|
||||||
- <icode>mouseY()</icode>: the vertical position of the mouse on the screen (as a floating point number).
|
|
||||||
|
|
||||||
Current mouse position can also be used to generate notes:
|
|
||||||
|
|
||||||
- <icode>noteX()</icode>: returns a MIDI note number (0-127) based on the horizontal position of the mouse on the screen.
|
|
||||||
- <icode>noteY()</icode>: returns a MIDI note number (0-127) based on the vertical position of the mouse on the screen.
|
|
||||||
|
|
||||||
## Low Frequency Oscillators
|
|
||||||
|
|
||||||
Low Frequency Oscillators (_LFOs_) are an important piece in any digital audio workstation or synthesizer. Topos implements some basic waveforms you can play with to automatically modulate your paremeters.
|
|
||||||
|
|
||||||
- <icode>sine(freq: number = 1, offset: number= 0): number</icode>: returns a sinusoïdal oscillation between <icode>-1</icode> and <icode>1</icode>.
|
|
||||||
- <icode>usine(freq: number = 1, offset: number= 0): number</icode>: returns a sinusoïdal oscillation between <icode>0</icode> and <icode>1</icode>. The <icode>u</icode> stands for _unipolar_.
|
|
||||||
|
|
||||||
\`\`\`javascript
|
|
||||||
mod(.25) && snd('cp').speed(1 + usine(0.25) * 2).out()
|
|
||||||
\`\`\`
|
|
||||||
|
|
||||||
- <icode>triangle(freq: number = 1, offset: number= 0): number</icode>: returns a triangle oscillation between <icode>-1</icode> and <icode>1</icode>.
|
|
||||||
- <icode>utriangle(freq: number = 1, offset: number= 0): number</icode>: returns a triangle oscillation between <icode>0</icode> and <icode>1</icode>. The <icode>u</icode> stands for _unipolar_.
|
|
||||||
|
|
||||||
\`\`\`javascript
|
|
||||||
mod(.25) && snd('cp').speed(1 + utriangle(0.25) * 2).out()
|
|
||||||
\`\`\`
|
|
||||||
|
|
||||||
- <icode>saw(freq: number = 1, offset: number= 0): number</icode>: returns a sawtooth-like oscillation between <icode>-1</icode> and <icode>1</icode>.
|
|
||||||
- <icode>usaw(freq: number = 1, offset: number= 0): number</icode>: returns a sawtooth-like oscillation between <icode>0</icode> and <icode>1</icode>. The <icode>u</icode> stands for _unipolar_.
|
|
||||||
|
|
||||||
\`\`\`javascript
|
|
||||||
mod(.25) && snd('cp').speed(1 + usaw(0.25) * 2).out()
|
|
||||||
\`\`\`
|
|
||||||
|
|
||||||
- <icode>square(freq: number = 1, offset: number= 0, duty: number = .5): number</icode>: returns a square wave oscillation between <icode>-1</icode> and <icode>1</icode>. You can also control the duty cycle using the <icode>duty</icode> parameter.
|
|
||||||
- <icode>usquare(freq: number = 1, offset: number= 0, duty: number = .5): number</icode>: returns a square wave oscillation between <icode>0</icode> and <icode>1</icode>. The <icode>u</icode> stands for _unipolar_. You can also control the duty cycle using the <icode>duty</icode> parameter.
|
|
||||||
|
|
||||||
\`\`\`javascript
|
|
||||||
mod(.25) && snd('cp').speed(1 + usquare(0.25, 0, 0.25) * 2).out()
|
|
||||||
\`\`\`
|
|
||||||
|
|
||||||
- <icode>noise()</icode>: returns a random value between -1 and 1.
|
|
||||||
|
|
||||||
\`\`\`javascript
|
|
||||||
mod(.25) && snd('cp').speed(1 + noise() * 2).out()
|
|
||||||
\`\`\`
|
|
||||||
|
|
||||||
## Probabilities
|
|
||||||
|
|
||||||
There are some simple functions to play with probabilities.
|
|
||||||
|
|
||||||
- <icode>rand(min: number, max:number)</icode>: returns a random number between <icode>min</icode> and <icode>max</icode>. Shorthand _r()_.
|
|
||||||
- <icode>irand(min: number, max:number)</icode>: returns a random integer between <icode>min</icode> and <icode>max</icode>. Shorthands _ir()_ or _rI()_.
|
|
||||||
- <icode>prob(p: number)</icode>: return <icode>true</icode> _p_% of time, <icode>false</icode> in other cases.
|
|
||||||
- <icode>toss()</icode>: throwing a coin. Head (<icode>true</icode>) or tails (<icode>false</icode>).
|
|
||||||
- <icode>seed(val: number|string)</icode>: sets the seed of the random number generator. You can use a number or a string. The same seed will always return the same sequence of random numbers.
|
|
||||||
|
|
||||||
Chance operators returning a boolean value are also available:
|
|
||||||
|
|
||||||
- <icode>odds(n: number, sec?: number)</icode>: returns true for every n (odds) (eg. 1/4 = 0.25) in given seconds (sec)
|
|
||||||
- <icode>almostNever(sec?: number)</icode>: returns true 0.1% in given seconds (sec)
|
|
||||||
- <icode>rarely(sec?: number)</icode>: returns true 1% in given seconds (sec)
|
|
||||||
- <icode>scaresly(sec?: number)</icode>: returns true 10% in given seconds (sec)
|
|
||||||
- <icode>sometimes(sec?: number)</icode>: returns true 50% in given seconds (sec)
|
|
||||||
- <icode>often(sec?: number)</icode>: returns true 75% in given seconds (sec)
|
|
||||||
- <icode>frequently(sec?: number)</icode>: returns true 90% in given seconds (sec)
|
|
||||||
- <icode>almostAlways(sec?: number)</icode>: returns true 99% in given seconds (sec)
|
|
||||||
|
|
||||||
## Math functions
|
|
||||||
|
|
||||||
- <icode>max(...values: number[]): number</icode>: returns the maximum value of a list of numbers.
|
|
||||||
- <icode>min(...values: number[]): number</icode>: returns the minimum value of a list of numbers.
|
|
||||||
- <icode>mean(...values: number[]): number</icode>: returns the arithmetic mean of a list of numbers.
|
|
||||||
- <icode>limit(value: number, min: number, max: number): number</icode>: Limits a value between a minimum and a maximum.
|
|
||||||
|
|
||||||
## Delay functions
|
|
||||||
|
|
||||||
- <icode>delay(ms: number, func: Function): void</icode>: Delays the execution of a function by a given number of milliseconds.
|
|
||||||
- <icode>delayr(ms: number, nb: number, func: Function): void</icode>: Delays the execution of a function by a given number of milliseconds, repeated a given number of times.
|
|
||||||
|
|
||||||
|
|
||||||
`;
|
|
||||||
|
|
||||||
const reference: string = `
|
|
||||||
# Reference
|
|
||||||
`;
|
|
||||||
|
|
||||||
const shortcuts: string = `
|
|
||||||
# Keybindings
|
|
||||||
|
|
||||||
Topos is made to be controlled entirely with a keyboard. It is recommanded to stop using the mouse as much as possible when you are _live coding_. Here is a list of the most important keybindings:
|
|
||||||
|
|
||||||
## Transport
|
|
||||||
|
|
||||||
- **Start** the transport: ${key_shortcut("Ctrl + P")}.
|
|
||||||
- **Pause** the transport: ${key_shortcut("Ctrl + S")}.
|
|
||||||
- **Rewind** the transport: ${key_shortcut("Ctrl + R")}.
|
|
||||||
|
|
||||||
## Moving in the interface
|
|
||||||
|
|
||||||
- Switch to a different universe: ${key_shortcut("Ctrl + B")}.
|
|
||||||
- Switch to the global script: ${key_shortcut("Ctrl + G")} or ${key_shortcut(
|
|
||||||
"F10"
|
|
||||||
)}.
|
|
||||||
- Switch to the local scripts: ${key_shortcut("Ctrl + L")} or ${key_shortcut(
|
|
||||||
"F11"
|
|
||||||
)}.
|
|
||||||
- Switch to the init script: ${key_shortcut("Ctrl + L")}.
|
|
||||||
- Switch to the note file: ${key_shortcut("Ctrl + N")}.
|
|
||||||
- Switch to a local file: ${key_shortcut("F1")} to ${key_shortcut("F9")}.
|
|
||||||
- Toggle the documentation: ${key_shortcut("Ctrl + D")}.
|
|
||||||
|
|
||||||
## Evaluating code
|
|
||||||
|
|
||||||
- Evaluate the current script: ${key_shortcut("Ctrl + Enter")}.
|
|
||||||
- Evaluate a local script: ${key_shortcut("Ctrl + F1")} to ${key_shortcut(
|
|
||||||
"Ctrl + F9"
|
|
||||||
)}.
|
|
||||||
|
|
||||||
## Special
|
|
||||||
|
|
||||||
- Switch the editor to Vim Mode: ${key_shortcut("Ctrl + V")}.
|
|
||||||
`;
|
|
||||||
|
|
||||||
export const documentation = {
|
|
||||||
introduction: introduction,
|
|
||||||
interface: software_interface,
|
|
||||||
code: code,
|
|
||||||
time: time,
|
|
||||||
sound: sound,
|
|
||||||
samples: samples,
|
|
||||||
synths: synths,
|
|
||||||
midi: midi,
|
|
||||||
functions: functions,
|
|
||||||
reference: reference,
|
|
||||||
shortcuts: shortcuts,
|
|
||||||
about: about,
|
|
||||||
};
|
|
||||||
Reference in New Issue
Block a user