46 Commits

Author SHA1 Message Date
83b6226a25 delete unused var 2023-12-19 22:33:41 +01:00
70cf7f2562 remove global variables madness 2023-12-19 22:27:08 +01:00
d977f2e8f2 Merge branch 'main' of github.com:Bubobubobubobubo/Topos 2023-12-19 22:09:34 +01:00
b47d041a99 turn off codemirror search 2023-12-19 22:09:11 +01:00
678b3305ac Merge branch 'main' of https://github.com/Bubobubobubobubo/Topos 2023-12-19 22:41:38 +02:00
facb30be3a Added counter to lists 2023-12-19 22:41:30 +02:00
ae96d20b70 Fixing PWA for good 2023-12-19 20:45:33 +01:00
83e901491f temp push 2023-12-19 19:38:32 +01:00
adf343e0bf don't log theme with randomTheme 2023-12-19 17:09:51 +01:00
ecd68ae7c6 remove thing 2023-12-19 15:20:04 +01:00
6f1f879f5e fix 2023-12-19 15:17:29 +01:00
b491637794 import everything preventively 2023-12-19 15:17:15 +01:00
88ad863664 Move stuff around 2023-12-19 15:13:08 +01:00
591332576e test again 2023-12-19 14:36:49 +01:00
a1e664eaa3 moving assets around... 2023-12-19 14:31:11 +01:00
c0cb7887c0 weird 2023-12-19 14:30:04 +01:00
251b7ed277 remove build step 2023-12-19 14:18:26 +01:00
37f8581b42 trying something new for PWA assets 2023-12-19 14:16:11 +01:00
6ca338fac4 some more tweaking for assets 2023-12-19 13:39:36 +01:00
d44d016357 try changing url again 2023-12-19 13:25:14 +01:00
024b083726 desperate move 2023-12-19 13:20:53 +01:00
4254136584 fix 2023-12-19 13:14:39 +01:00
2606b8f989 fix again 2023-12-19 13:13:10 +01:00
b8e197d64a continue fixing 2023-12-19 13:06:09 +01:00
620ca7af59 continue to fix PWA 2023-12-19 01:26:44 +01:00
6305e0ce65 Attempting to fix PWA configuration 2023-12-19 01:22:17 +01:00
e557e5565b Fixing ziffers default sync 2023-12-18 23:03:15 +02:00
331ddab544 Added once() method 2023-12-18 22:28:48 +02:00
f46565f5c2 Harmonize ration param name 2023-12-18 21:38:39 +02:00
8819a159ff Add pulseLocation() for visualizations 2023-12-18 20:46:35 +02:00
caabfc2e65 Added missing params 2023-12-18 19:41:22 +02:00
7a5f15b29d Fix for donuts 2023-12-18 19:35:22 +02:00
20d2e3a176 Updating cavas docs 2023-12-18 19:30:15 +02:00
62ed707c59 Load documentation from fragment links 2023-12-18 17:59:28 +02:00
0ba7ed2756 merge 2023-12-18 17:34:46 +02:00
9458733492 Merge branch 'main' into doclinks 2023-12-18 17:33:47 +02:00
7086682336 Merge branch 'main' of https://github.com/Bubobubobubobubo/Topos 2023-12-18 17:28:49 +02:00
3c602dc63b Added some visualization features and documentation 2023-12-18 17:28:36 +02:00
Raphaël Forment
5456410d08 Merge pull request #109 from edelveart/generators
docs (tonnetz-generators)
2023-12-17 23:57:39 +01:00
Edgar Delgado Vega
61fb6365a0 Merge branch 'main' of github.com:Bubobubobubobubo/topos into generators 2023-12-17 17:52:26 -05:00
Edgar Delgado Vega
11be35c677 docs(generators-tonnetz): fix some typos and example 2023-12-17 17:51:12 -05:00
Raphaël Forment
122cd55ea2 Merge pull request #108 from edelveart/generators
docs(generators): add examples of continuos attract
2023-12-17 23:36:18 +01:00
Edgar Delgado Vega
f9bce56f9e docs(generators): add an example of a continuous attractor and a discrete attractor 2023-12-17 17:29:19 -05:00
ad6f8a5e91 HTML links should stand out in the documentation 2023-12-17 20:20:35 +01:00
Raphaël Forment
b5988d07f6 Merge pull request #107 from Bubobubobubobubo/system-switch
Clock backtracking: removing Zyklus
2023-12-17 17:41:57 +01:00
04142dbbbc Added check for documentation links 2023-12-17 13:13:39 +02:00
49 changed files with 1308 additions and 379 deletions

View File

@@ -47,9 +47,6 @@ jobs:
with:
path: "main"
- name: Copy favicon folder
run: cp -r main/favicon ./dist/
- name: Deploy to GitHub Pages
uses: peaceiris/actions-gh-pages@v3
with:

View File

@@ -19,52 +19,53 @@
---
Topos is a web based live coding environment. Topos is capable of many things:
Topos is a web based live coding environment designed to be installation-free, independant and fun. Topos is loosely based on the [Monome Teletype](https://monome.org/docs/teletype/). The application follows the same operating principle, but adapts it to the rich multimedia context offered by web browsers. Topos is capable of many things:
- it is a music sequencer made for improvisation and composition alike
- it is a synthesizer capable of additive, substractive, FM and wavetable
synthesis, backed up by a [powerful web based audio engine](https://www.npmjs.com/package/superdough)
- it can also generate video thanks to [Hydra](https://hydra.ojack.xyz/) and
custom oscilloscopes, frequency visualizers and image sequencing capabilities
- it can be used to sequence other MIDI devices (and soon.. OSC!)
- it is a generative/algorithmic music sequencer made for **improvisation** and **composition** alike
- it is a synthesizer capable of _additive_, _substractive_, _FM_ and _wavetable
synthesis_, backed up by a [powerful web based audio engine](https://www.npmjs.com/package/superdough)
- it can also generate video thanks to [Hydra](https://hydra.ojack.xyz/),
oscilloscopes, frequency visualizers and image/canvas sequencing capabilities
- it can be used to sequence other MIDI and OSC devices (the latter using a **NodeJS** script)
- it is made to be used without the need of installing anything, always ready at
[https://topos.live](https://topos.live)
- Topos is also an emulation and personal extension of the [Monome Teletype](https://monome.org/docs/teletype/)
---
![Screenshot](https://github.com/Bubobubobubobubo/Topos/blob/main/img/topos_gif.gif)
![Screenshot](https://github.com/Bubobubobubobubo/Topos/blob/main/src/assets/topos_gif.gif)
## Disclaimer
**Topos** is still a young project developed by two hobbyists :) Contributions are welcome! We wish to be as inclusive and welcoming as possible to your ideas and suggestions! The software is working quite well and we are continuously striving to improve it.
**Topos** is still a young and experimental project developed by two hobbyists :) Contributions are welcome! We wish to be as inclusive and welcoming as possible to your ideas and suggestions! The software is working quite well and we are continuously striving to improve it. Note that most features are rather experimental and that we don't really have any classical training in web development.
## Installation (for devs and contributors)
## Local Installation (for devs and contributors)
To run the application, you will need to install [Node.js](https://nodejs.org/en/) and [Yarn](https://yarnpkg.com/en/). Then, clone the repository and run:
- `yarn install`
- `yarn run dev`
To build the application for production, you will need to install [Node.js](https://nodejs.org/en/) and [Yarn](https://yarnpkg.com/en/). Then, clone the repository and run:
You are good to go. The application will update itself automatically with every change to the codebase. To test the production version of the applicationn, you will need to install [Node.js](https://nodejs.org/en/) and [Yarn](https://yarnpkg.com/en/). Then, clone the repository and run:
- `yarn run build`
- `yarn run start`
Always run a build before committing to check for compiler errors. The automatic deployment on the `main` branch will not accept compiler errors!
If the build passes, you can be sure that it will also pass our **CI** pipeline that deploys the application to [https://topos.live](https://topos.live). Always run a build before committing to check for compiler errors. The automatic deployment on the `main` branch will not accept compiler errors!
To build a standalone browser application using [Tauri](https://tauri.app/), you will need to have [Node.js](https://nodejs.org/en/), [Yarn](https://yarnpkg.com/en/) and [Rust](https://www.rust-lang.org/) installed. Then, clone the repository and run:
## Tauri version
Topos can also be compiled as a standalone application using [Tauri](https://tauri.app/). You will need [Node.js](https://nodejs.org/en/), [Yarn](https://yarnpkg.com/en/) and [Rust](https://www.rust-lang.org/) to be installed on your computer. Then, clone the repository and run:
- `yarn tauri build`
- `yarn tauri dev`
The `tauri` version is only here to quickstart future developments but nothing has been done yet.
The `tauri` version has never been fleshed out. It's a template for later developments if Topos ever wants to escape from the web :)
## Docker
### Run the application
To run the **Docker** version, run the following command:
`docker run -p 8001:80 yassinsiouda/topos:latest`
`docker run -p 8001:80 bubobubobubo/topos:latest`
### Build and run the prod image
@@ -72,8 +73,7 @@ The `tauri` version is only here to quickstart future developments but nothing
### Build and run the dev image
**First installation**
First you need to map node_modules to your local machine for your ide intellisense to work properly
First you need to map `node_modules` to your local machine for your IDE IntelliSense to work properly :
```bash
docker compose --profile dev up -d
@@ -81,8 +81,21 @@ docker cp topos-dev:/app/node_modules .
docker compose --profile dev down
```
**Then**
then run the following command:
```bash
docker compose --profile dev up
```
Note that a Docker version of Topos is automatically generated everytime a commit is done on the `main` branch.
## Credits
- Felix Roos for the [SuperDough](https://www.npmjs.com/package/superdough) audio engine.
- Frank Force for the [ZzFX](https://github.com/KilledByAPixel/ZzFX) synthesizer.
- Kristoffer Ekstrand for the [AKWF](https://www.adventurekid.se/akrt/waveforms/adventure-kid-waveforms/) waveforms.
- Ryan Kirkbride for some of the audio samples in the [Dough-Fox](https://github.com/Bubobubobubobubo/Dough-Fox) sample pack, taken from [here](https://github.com/Qirky/FoxDot/tree/master/FoxDot/snd).
- Adel Faure for the [JGS](https://adelfaure.net/https://adelfaure.net/) font.
- Raphaël Bastide for the [Steps Mono](https://github.com/raphaelbastide/steps-mono/) font.
Many thanks to all the contributors and folks who tried the software already :)

View File

@@ -1,19 +0,0 @@
{
"name": "",
"short_name": "",
"icons": [
{
"src": "/android-chrome-192x192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "/android-chrome-512x512.png",
"sizes": "512x512",
"type": "image/png"
}
],
"theme_color": "#ffffff",
"background_color": "#ffffff",
"display": "standalone"
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 590 KiB

View File

@@ -1,13 +1,14 @@
<!doctype html>
<html lang="en">
<head>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta name="description" content="Topos is a live coding environment inspired by the Monome Teletype.">
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Topos</title>
<meta name="description" content="Topos is a live coding environment inspired by the Monome Teletype.">
<meta charset="UTF-8" />
<link rel="apple-touch-icon" sizes="180x180" href="favicon/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="favicon/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="favicon/favicon-16x16.png">
<link rel="icon" href="/favicon/favicon.ico" sizes="48x48" ><!-- REVISED (Aug 11, 2023)! -->
<link rel="icon" href="/favicon/favicon.svg" sizes="any" type="image/svg+xml"><!-- REVISED (Aug 11, 2023)! -->
<link rel="apple-touch-icon" href="/favicon/apple-touch-icon.png"/>
<link rel="manifest" href="/manifest.webmanifest" />
<link rel="mask-icon" href="favicon/safari-pinned-tab.svg" color="#5bbad5">
<meta name="msapplication-TileColor" content="#da532c">
<meta name="theme-color" content="#ffffff">
@@ -237,6 +238,7 @@
<div class="flex flex-col">
<a rel="noopener noreferrer" id="docs_synchronisation" class="doc_subheader">Synchronisation</a>
<a rel="noopener noreferrer" id="docs_oscilloscope" class="doc_subheader">Oscilloscope</a>
<a rel="noopener noreferrer" id="docs_visualization" class="doc_header">Visualization</a>
<a rel="noopener noreferrer" id="docs_bonus" class="doc_header">Bonus/Trivia</a>
<a rel="noopener noreferrer" id="docs_about" class="doc_header">About Topos</a>
</div>

View File

@@ -1,24 +0,0 @@
{
"name": "Topos",
"short_name": "Topos",
"description": "Live coding environment",
"theme_color": "#ffffff",
"background_color": "#ffffff",
"display": "standalone",
"scope": "/",
"start_url": "/",
"icons": [
{
"src": "./favicon/android-chrome-192x192.png",
"sizes": "192x192",
"type": "image/png",
"purpose": "any maskable"
},
{
"src": "./favicon/android-chrome-512x512.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "any maskable"
}
]
}

View File

@@ -14,7 +14,7 @@
"typescript": "^5.2.2",
"vite": "^4.4.5",
"vite-plugin-compression": "^0.5.1",
"vite-plugin-pwa": "^0.16.7"
"vite-plugin-pwa": "^0.17.4"
},
"dependencies": {
"@codemirror/lang-javascript": "^6.1.9",

View File

Before

Width:  |  Height:  |  Size: 31 KiB

After

Width:  |  Height:  |  Size: 31 KiB

View File

Before

Width:  |  Height:  |  Size: 121 KiB

After

Width:  |  Height:  |  Size: 121 KiB

View File

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 23 KiB

View File

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

Before

Width:  |  Height:  |  Size: 3.0 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

View File

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

View File

@@ -0,0 +1,46 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
width="48.000000pt" height="48.000000pt" viewBox="0 0 48.000000 48.000000"
preserveAspectRatio="xMidYMid meet">
<g transform="translate(0.000000,48.000000) scale(0.100000,-0.100000)"
fill="#000000" stroke="none">
<path d="M163 470 c-13 -5 -23 -12 -23 -15 0 -3 46 -5 102 -5 80 0 99 3 90 12
-15 15 -139 21 -169 8z"/>
<path d="M91 391 c-16 -16 -19 -32 -18 -84 1 -53 -2 -69 -18 -87 -15 -16 -20
-38 -22 -83 0 -33 2 -56 5 -50 9 13 44 13 39 -1 -2 -6 -18 -11 -34 -11 -39 0
-50 -10 -33 -30 8 -10 30 -15 60 -15 26 0 64 -7 83 -15 43 -18 125 -20 164 -3
15 6 57 14 93 17 73 7 87 24 51 60 -12 12 -21 17 -21 13 0 -4 5 -13 12 -20 8
-8 8 -12 1 -12 -18 0 -25 34 -9 45 10 7 12 16 6 25 -5 8 -7 24 -4 35 3 13 -2
28 -15 39 -12 11 -21 27 -21 36 0 9 -5 21 -11 27 -8 8 -8 17 0 31 17 32 13 56
-12 80 -31 29 -63 28 -91 -3 -16 -17 -34 -25 -56 -25 -22 0 -40 8 -56 25 -28
30 -67 32 -93 6z m89 -16 c19 -23 5 -29 -24 -10 -16 10 -33 14 -47 10 -14 -5
-19 -4 -15 4 10 16 72 13 86 -4z m211 -7 c12 -22 11 -22 -8 -5 -25 21 -41 22
-65 0 -22 -19 -36 -10 -18 12 20 24 77 19 91 -7z m-264 -30 c-3 -7 -5 -2 -5
12 0 14 2 19 5 13 2 -7 2 -19 0 -25z m229 -5 c-11 -11 -19 6 -11 24 8 17 8 17
12 0 3 -10 2 -21 -1 -24z m-80 -8 c4 -8 10 -12 15 -9 5 3 9 0 9 -6 0 -14 60
-40 77 -33 7 3 13 -2 13 -11 0 -13 -6 -15 -27 -9 -36 9 -210 9 -245 0 -22 -6
-28 -4 -28 9 0 8 6 14 13 11 6 -2 25 2 40 10 15 8 32 11 37 8 6 -4 7 1 3 11
-4 12 -3 15 5 10 7 -4 15 -1 18 8 8 21 63 21 70 1z m83 -91 c10 -9 -37 -33
-77 -39 -55 -8 -117 2 -155 26 l-30 18 54 4 c56 4 201 -2 208 -9z m-286 -30
c-15 -7 21 -44 42 -44 8 0 15 -4 15 -10 0 -16 -33 -11 -59 9 -25 19 -24 52 1
50 9 0 10 -2 1 -5z m355 -21 c2 -14 -4 -23 -17 -28 -12 -3 -21 -13 -21 -21 0
-19 -16 -18 -24 1 -5 15 -31 16 -53 2 -8 -5 -13 -4 -13 2 0 6 5 12 10 12 6 1
15 3 20 4 6 1 18 3 28 4 25 1 66 37 52 44 -6 3 -5 4 2 3 7 -1 14 -12 16 -23z
m-255 -37 c4 -10 1 -13 -9 -9 -7 3 -14 9 -14 14 0 14 17 10 23 -5z m42 -6 c3
-5 1 -10 -4 -10 -6 0 -11 5 -11 10 0 6 2 10 4 10 3 0 8 -4 11 -10z m35 0 c0
-5 -4 -10 -10 -10 -5 0 -10 5 -10 10 0 6 5 10 10 10 6 0 10 -4 10 -10z m-170
-10 c0 -5 -5 -10 -11 -10 -5 0 -7 5 -4 10 3 6 8 10 11 10 2 0 4 -4 4 -10z
m215 -39 c-6 -5 -25 10 -25 20 0 5 6 4 14 -3 8 -7 12 -15 11 -17z m45 8 c0
-14 -16 -11 -29 5 -10 12 -8 13 8 9 12 -3 21 -9 21 -14z m-220 1 c0 -5 -5 -10
-11 -10 -5 0 -7 5 -4 10 3 6 8 10 11 10 2 0 4 -4 4 -10z m40 0 c0 -5 -4 -10
-10 -10 -5 0 -10 5 -10 10 0 6 5 10 10 10 6 0 10 -4 10 -10z m215 0 c3 -5 2
-10 -4 -10 -5 0 -13 5 -16 10 -3 6 -2 10 4 10 5 0 13 -4 16 -10z m43 -10 c7
-11 10 -20 6 -20 -7 0 -34 27 -34 34 0 13 16 5 28 -14z m-160 -17 c-10 -2 -26
-2 -35 0 -10 3 -2 5 17 5 19 0 27 -2 18 -5z m99 1 c-3 -3 -12 -4 -19 -1 -8 3
-5 6 6 6 11 1 17 -2 13 -5z m40 0 c-3 -3 -12 -4 -19 -1 -8 3 -5 6 6 6 11 1 17
-2 13 -5z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.9 KiB

View File

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 21 KiB

View File

Before

Width:  |  Height:  |  Size: 603 B

After

Width:  |  Height:  |  Size: 603 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

View File

@@ -0,0 +1,37 @@
{
"name": "Topos",
"short_name": "Topos",
"icons": [
{
"src": "favicon/android-chrome-192x192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "favicon/android-chrome-512x512.png",
"sizes": "512x512",
"type": "image/png"
}
],
"display": "standalone",
"start_url": "/",
"scope": "/",
"theme_color": "#ffffff",
"background_color": "#ffffff",
"description": "Topos is a web based live coding platform",
"screenshots": [
{
"src": "favicon/screenshot_miniature.png",
"sizes": "640x320",
"type": "image/gif",
"form_factor": "wide",
"label": "Topos application"
},
{
"src": "favicon/topos_code.png",
"sizes": "1280x768",
"type": "image/gif",
"label": "Topos code"
}
]
}

File diff suppressed because it is too large Load Diff

View File

@@ -17,6 +17,7 @@ import { oscilloscope } from "./documentation/more/oscilloscope";
import { synchronisation } from "./documentation/more/synchronisation";
import { about } from "./documentation/more/about";
import { bonus } from "./documentation/more/bonus";
import { visualization } from "./documentation/more/visualization";
import { chaining } from "./documentation/patterns/chaining";
import { interaction } from "./documentation/basics/interaction";
import { time } from "./documentation/learning/time/time";
@@ -74,6 +75,47 @@ export const makeExampleFactory = (application: Editor): Function => {
return make_example;
};
export const documentation_pages = [
"introduction",
"sampler",
"amplitude",
"audio_basics",
"filters",
"effects",
"interface",
"interaction",
"code",
"time",
"linear",
"cyclic",
"longform",
"synths",
"chaining",
"patterns",
"ziffers_basics",
"ziffers_scales",
"ziffers_rhythm",
"ziffers_algorithmic",
"ziffers_tonnetz",
"ziffers_syncing",
"midi",
"osc",
"functions",
"generators",
"lfos",
"probabilities",
"variables",
"synchronisation",
"mouse",
"shortcuts",
"about",
"bonus",
"oscilloscope",
"sample_list",
"loading_samples",
"visualization"
];
export const documentation_factory = (application: Editor) => {
/**
* Creates the documentation for the given application.
@@ -117,6 +159,7 @@ export const documentation_factory = (application: Editor) => {
audio_basics: audio_basics(application),
synchronisation: synchronisation(application),
bonus: bonus(application),
visualization: visualization(application),
sample_list: sample_list(application),
sample_banks: sample_banks(application),
loading_samples: loading_samples(application),
@@ -179,13 +222,16 @@ export const updateDocumentationContent = (app: Editor, bindings: any) => {
auto_detection: false
}), ...bindings],
});
console.log(app.currentDocumentationPane);
if(Object.keys(app.docs).length === 0) {
app.docs = documentation_factory(app);
}
function _update_and_assign(callback: Function) {
function _update_and_assign(callback: Function) {
const converted_markdown = converter.makeHtml(
app.docs[app.currentDocumentationPane],
);
callback(converted_markdown)
callback(converted_markdown)
}
_update_and_assign((e: string)=> {
let display_content = e === undefined ? loading_message : e;

View File

@@ -83,7 +83,7 @@ export const createDocumentationStyle = (app: Editor) => {
p: "lg:text-2xl text-base text-white lg:mx-6 mx-2 my-4 leading-normal",
warning:
"animate-pulse lg:text-2xl font-bold text-brightred lg:mx-6 mx-2 my-4 leading-normal",
a: "lg:text-2xl text-base text-white",
a: "lg:text-2xl text-base text-brightred",
code: `lg:my-4 sm:my-1 text-base lg:text-xl block whitespace-pre overflow-x-hidden`,
icode:
"lg:my-1 my-1 lg:text-xl sm:text-xs text-brightwhite font-mono bg-brightblack",

View File

@@ -20,7 +20,7 @@ import {
HighlightStyle,
} from "@codemirror/language";
import { defaultKeymap, historyKeymap, history } from "@codemirror/commands";
import { searchKeymap, highlightSelectionMatches } from "@codemirror/search";
import { highlightSelectionMatches } from "@codemirror/search";
import {
autocompletion,
closeBrackets,
@@ -259,7 +259,7 @@ export const editorSetup: Extension = (() => [
highlightActiveLine(),
highlightSelectionMatches(),
keymap.of([
...searchKeymap,
// ...searchKeymap,
...closeBracketsKeymap,
...defaultKeymap,
...historyKeymap,

View File

@@ -4,6 +4,7 @@ import { type Editor } from "./main";
import colors from "./colors.json";
import {
documentation_factory,
documentation_pages,
hideDocumentation,
showDocumentation,
updateDocumentationContent,
@@ -23,19 +24,11 @@ import { tryEvaluate } from "./Evaluator";
import { inlineHoveringTips } from "./documentation/inlineHelp";
import { lineNumbers } from "@codemirror/view";
import { jsCompletions } from "./EditorSetup";
import { createDocumentationStyle } from "./DomElements";
import { saveState } from "./WindowBehavior";
import { registerSamplesFromDB, samplesDBConfig, uploadSamplesToDB } from "./IO/SampleLoading";
export const installInterfaceLogic = (app: Editor) => {
// Initialize style
const documentationStyle = createDocumentationStyle(app);
const bindings = Object.keys(documentationStyle).map((key) => ({
type: "output",
regex: new RegExp(`<${key}([^>]*)>`, "g"),
//@ts-ignore
replace: (match, p1) => `<${key} class="${documentationStyle[key]}" ${p1}>`,
}));
(app.interface.line_numbers_checkbox as HTMLInputElement).checked =
app.settings.line_numbers;
@@ -537,60 +530,24 @@ export const installInterfaceLogic = (app: Editor) => {
tryEvaluate(app, app.universes[app.selected_universe.toString()].init);
[
"introduction",
"sampler",
"amplitude",
"audio_basics",
"filters",
"effects",
"interface",
"interaction",
"code",
"time",
"linear",
"cyclic",
"longform",
"synths",
"chaining",
"patterns",
"ziffers_basics",
"ziffers_scales",
"ziffers_rhythm",
"ziffers_algorithmic",
"ziffers_tonnetz",
"ziffers_syncing",
"midi",
"osc",
"functions",
"generators",
"lfos",
"probabilities",
"variables",
"synchronisation",
"mouse",
"shortcuts",
"about",
"bonus",
"oscilloscope",
"sample_list",
"loading_samples",
].forEach((e) => {
documentation_pages.forEach((e) => {
let name = `docs_` + e;
// Check if the element exists
let element = document.getElementById(name);
if (element) {
element.addEventListener("click", async () => {
// Clear query params & set id as hash paremeter for uri
window.history.replaceState({}, "", window.location.pathname);
window.location.hash = e;
app.docs = documentation_factory(app);
app.currentDocumentationPane = e;
if (name !== "docs_sample_list") {
app.currentDocumentationPane = e;
updateDocumentationContent(app, bindings);
updateDocumentationContent(app, app.bindings);
} else {
console.log("Loading samples!");
await loadSamples().then(() => {
app.docs = documentation_factory(app);
app.currentDocumentationPane = e;
updateDocumentationContent(app, bindings);
updateDocumentationContent(app, app.bindings);
});
}
});

View File

@@ -105,6 +105,7 @@ export const registerOnKeyDown = (app: Editor) => {
if (event.key === "Enter" && event.shiftKey && event.ctrlKey) {
event.preventDefault();
app.currentFile().candidate = app.view.state.doc.toString();
app.api.onceEvaluator = true;
tryEvaluate(app, app.currentFile());
app.flashBackground("#404040", 200);
}
@@ -114,6 +115,7 @@ export const registerOnKeyDown = (app: Editor) => {
event.preventDefault();
app.api.clearPatternCache();
app.currentFile().candidate = app.view.state.doc.toString();
app.api.onceEvaluator = true;
tryEvaluate(app, app.currentFile());
app.flashBackground("#404040", 200);
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 121 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

BIN
src/assets/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

46
src/assets/favicon.svg Normal file
View File

@@ -0,0 +1,46 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
width="48.000000pt" height="48.000000pt" viewBox="0 0 48.000000 48.000000"
preserveAspectRatio="xMidYMid meet">
<g transform="translate(0.000000,48.000000) scale(0.100000,-0.100000)"
fill="#000000" stroke="none">
<path d="M163 470 c-13 -5 -23 -12 -23 -15 0 -3 46 -5 102 -5 80 0 99 3 90 12
-15 15 -139 21 -169 8z"/>
<path d="M91 391 c-16 -16 -19 -32 -18 -84 1 -53 -2 -69 -18 -87 -15 -16 -20
-38 -22 -83 0 -33 2 -56 5 -50 9 13 44 13 39 -1 -2 -6 -18 -11 -34 -11 -39 0
-50 -10 -33 -30 8 -10 30 -15 60 -15 26 0 64 -7 83 -15 43 -18 125 -20 164 -3
15 6 57 14 93 17 73 7 87 24 51 60 -12 12 -21 17 -21 13 0 -4 5 -13 12 -20 8
-8 8 -12 1 -12 -18 0 -25 34 -9 45 10 7 12 16 6 25 -5 8 -7 24 -4 35 3 13 -2
28 -15 39 -12 11 -21 27 -21 36 0 9 -5 21 -11 27 -8 8 -8 17 0 31 17 32 13 56
-12 80 -31 29 -63 28 -91 -3 -16 -17 -34 -25 -56 -25 -22 0 -40 8 -56 25 -28
30 -67 32 -93 6z m89 -16 c19 -23 5 -29 -24 -10 -16 10 -33 14 -47 10 -14 -5
-19 -4 -15 4 10 16 72 13 86 -4z m211 -7 c12 -22 11 -22 -8 -5 -25 21 -41 22
-65 0 -22 -19 -36 -10 -18 12 20 24 77 19 91 -7z m-264 -30 c-3 -7 -5 -2 -5
12 0 14 2 19 5 13 2 -7 2 -19 0 -25z m229 -5 c-11 -11 -19 6 -11 24 8 17 8 17
12 0 3 -10 2 -21 -1 -24z m-80 -8 c4 -8 10 -12 15 -9 5 3 9 0 9 -6 0 -14 60
-40 77 -33 7 3 13 -2 13 -11 0 -13 -6 -15 -27 -9 -36 9 -210 9 -245 0 -22 -6
-28 -4 -28 9 0 8 6 14 13 11 6 -2 25 2 40 10 15 8 32 11 37 8 6 -4 7 1 3 11
-4 12 -3 15 5 10 7 -4 15 -1 18 8 8 21 63 21 70 1z m83 -91 c10 -9 -37 -33
-77 -39 -55 -8 -117 2 -155 26 l-30 18 54 4 c56 4 201 -2 208 -9z m-286 -30
c-15 -7 21 -44 42 -44 8 0 15 -4 15 -10 0 -16 -33 -11 -59 9 -25 19 -24 52 1
50 9 0 10 -2 1 -5z m355 -21 c2 -14 -4 -23 -17 -28 -12 -3 -21 -13 -21 -21 0
-19 -16 -18 -24 1 -5 15 -31 16 -53 2 -8 -5 -13 -4 -13 2 0 6 5 12 10 12 6 1
15 3 20 4 6 1 18 3 28 4 25 1 66 37 52 44 -6 3 -5 4 2 3 7 -1 14 -12 16 -23z
m-255 -37 c4 -10 1 -13 -9 -9 -7 3 -14 9 -14 14 0 14 17 10 23 -5z m42 -6 c3
-5 1 -10 -4 -10 -6 0 -11 5 -11 10 0 6 2 10 4 10 3 0 8 -4 11 -10z m35 0 c0
-5 -4 -10 -10 -10 -5 0 -10 5 -10 10 0 6 5 10 10 10 6 0 10 -4 10 -10z m-170
-10 c0 -5 -5 -10 -11 -10 -5 0 -7 5 -4 10 3 6 8 10 11 10 2 0 4 -4 4 -10z
m215 -39 c-6 -5 -25 10 -25 20 0 5 6 4 14 -3 8 -7 12 -15 11 -17z m45 8 c0
-14 -16 -11 -29 5 -10 12 -8 13 8 9 12 -3 21 -9 21 -14z m-220 1 c0 -5 -5 -10
-11 -10 -5 0 -7 5 -4 10 3 6 8 10 11 10 2 0 4 -4 4 -10z m40 0 c0 -5 -4 -10
-10 -10 -5 0 -10 5 -10 10 0 6 5 10 10 10 6 0 10 -4 10 -10z m215 0 c3 -5 2
-10 -4 -10 -5 0 -13 5 -16 10 -3 6 -2 10 4 10 5 0 13 -4 16 -10z m43 -10 c7
-11 10 -20 6 -20 -7 0 -34 27 -34 34 0 13 16 5 28 -14z m-160 -17 c-10 -2 -26
-2 -35 0 -10 3 -2 5 17 5 19 0 27 -2 18 -5z m99 1 c-3 -3 -12 -4 -19 -1 -8 3
-5 6 6 6 11 1 17 -2 13 -5z m40 0 c-3 -3 -12 -4 -19 -1 -8 3 -5 6 6 6 11 1 17
-2 13 -5z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

View File

@@ -0,0 +1,15 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
width="850.000000pt" height="850.000000pt" viewBox="0 0 850.000000 850.000000"
preserveAspectRatio="xMidYMid meet">
<metadata>
Created by potrace 1.14, written by Peter Selinger 2001-2017
</metadata>
<g transform="translate(0.000000,850.000000) scale(0.100000,-0.100000)"
fill="#000000" stroke="none">
<path d="M0 4250 l0 -3770 4250 0 4250 0 0 3770 0 3770 -4250 0 -4250 0 0
-3770z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 603 B

BIN
src/assets/topos_code.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 194 KiB

View File

Before

Width:  |  Height:  |  Size: 427 KiB

After

Width:  |  Height:  |  Size: 427 KiB

BIN
src/assets/topos_gif.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 MiB

View File

@@ -408,8 +408,7 @@ export class Player extends AbstractEvent {
}
sync(value: string | Function, manualSync: boolean = true) {
if(typeof value === "string") {
if(typeof value === "string" && manualSync) {
if(manualSync) {
const cueTime = this.app.api.cueTimes[value];
if(cueTime) {
@@ -420,11 +419,11 @@ export class Player extends AbstractEvent {
}
return this;
}
if (this.atTheBeginning() && this.notStarted()) {
const origin = this.app.clock.pulses_since_origin;
if (origin > 0) {
const syncPattern = this.app.api.patternCache.get(value.name) as Player;
const syncName = typeof value === "function" ? value.name : value;
const syncPattern = this.app.api.patternCache.get(syncName) as Player;
if (syncPattern) {
const syncPatternDuration = syncPattern.ziffers.duration;
const syncPatternStart = syncPattern.startCallTime;

View File

@@ -415,6 +415,8 @@ beat(2) :: sound('zzfx').zzfx([3.62,,452,.16,.1,.21,,2.5,,,403,.05,.29,,,,.17,.3
Topos can also speak using the [Web Speech API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Speech_API). There are two ways to use speech synthesis:
Speech synthesis API can crash your browser if you use it too much. To avoid crashing the calls should be limited using methods like beat() or run it only once using once().
- <ic>speak(text: string, lang: string, voice: number, rate: number, pitch: number, volume: number)</ic>
- <ic>text</ic>: the text you would like to synthesize (_e.g_ <ic>"Wow, Topos can speak!"</ic>).
- <ic>lang</ic>: language code, for example <ic>en</ic> for English, <ic>fr</ic> for French or with the country code for example British English <ic>en-GB</ic>. See supported values from the [list](https://cloud.google.com/speech-to-text/docs/speech-to-text-supported-languages).
@@ -426,7 +428,7 @@ Topos can also speak using the [Web Speech API](https://developer.mozilla.org/en
${makeExample(
"Hello world!",
`
beat(4) :: speak("Hello world!")
once() && speak("Hello world!")
`,
true,
)}
@@ -434,7 +436,7 @@ beat(4) :: speak("Hello world!")
${makeExample(
"Let's hear people talking about Topos",
`
beat(2) :: speak("Topos!","fr",irand(0,5))
beat(2) && speak("Topos!","fr",irand(0,5))
`,
true,
)}
@@ -445,7 +447,7 @@ You can also use speech by chaining methods to a string:
${makeExample(
"Foobaba is the real deal",
`
onbeat(4) :: "Foobaba".voice(irand(0,10)).speak()
onbeat(4) && "Foobaba".voice(irand(0,10)).speak()
`,
true,
)}
@@ -458,7 +460,7 @@ ${makeExample(
const object = ["happy","sad","tired"].pick()
const sentence = subject+" "+verb+" "+" "+object
beat(6) :: sentence.pitch(0).rate(0).voice([0,2].pick()).speak()
beat(6) && sentence.pitch(0).rate(0).voice([0,2].pick()).speak()
`,
true,
)}
@@ -474,7 +476,7 @@ ${makeExample(
"Flamboyant", "Cosmique", "Croissant!"
];
onbeat(4) :: croissant.bar()
onbeat(4) && croissant.bar()
.lang("fr")
.volume(rand(0.2,2.0))
.rate(rand(.4,.6))

View File

@@ -0,0 +1,227 @@
import { type Editor } from "../../main";
import { key_shortcut, makeExampleFactory } from "../../Documentation";
export const visualization = (application: Editor): string => {
const makeExample = makeExampleFactory(application);
return `
# Vizualisation
While Topos is mainly being developed as a live coding environment for algorithmic music composition, it also includes some features for live code visualizations. This section will introduce you to these features.
## Hydra Visual Live Coding
<div class="mx-12 bg-neutral-600 rounded-lg flex flex-col items-center justify-center">
<warning>⚠️ This feature can generate flashing images that could trigger photosensitivity or epileptic seizures. ⚠️ </warning>
</div>
[Hydra](https://hydra.ojack.xyz/?sketch_id=mahalia_1) is a popular live-codable video synthesizer developed by [Olivia Jack](https://ojack.xyz/) and other contributors. It follows an analog synthesizer patching metaphor to encourage live coding complex shaders. Being very easy to use, extremely powerful and also very rewarding to use, Hydra has become a popular choice for adding visuals into a live code performance.
${makeExample(
"Hydra integration",
`beat(4) :: hydra.osc(3, 0.5, 2).out()`,
false,
)}
Close the documentation to see the effect: ${key_shortcut(
"Ctrl+D",
)}! **Boom, all shiny!**
Be careful not to call <ic>hydra</ic> too often as it can impact performances. You can use any rhythmical function like <ic>beat()</ic> function to limit the number of function calls. You can write any Topos code like <ic>[1,2,3].beat()</ic> to bring some life and movement in your Hydra sketches.
Stopping **Hydra** is simple:
${makeExample(
"Stopping Hydra",
`
beat(4) :: stop_hydra() // this one
beat(4) :: hydra.hush() // or this one
`,
false,
)}
### Changing the resolution
You can change Hydra resolution using this simple method:
${makeExample(
"Changing Hydra resolution",
`hydra.setResolution(1024, 768)`,
false,
)}
### Hydra documentation
I won't teach Hydra. You can find some great resources directly on the [Hydra website](https://hydra.ojack.xyz/):
- [Hydra interactive documentation](https://hydra.ojack.xyz/docs/)
- [List of Hydra Functions](https://hydra.ojack.xyz/api/)
- [Source code on GitHub](https://github.com/hydra-synth/hydra)
### The Hydra namespace
In comparison with the basic Hydra editor, please note that you have to prefix all Hydra functions with <ic>hydra.</ic> to avoid conflicts with Topos functions. For example, <ic>osc()</ic> becomes <ic>hydra.osc()</ic>.
${makeExample("Hydra namespace", `hydra.voronoi(20).out()`, true)}
## GIF player
Topos embeds a small <ic>.gif</ic> picture player with a small API. GIFs are automatically fading out after the given duration. Look at the following example:
${makeExample(
"Playing many gifs",
`
beat(0.25)::gif({
url:v('gif')[$(1)%6], // Any URL will do!
opacity: r(0.5, 1), // Opacity (0-1)
size:"300px", // CSS size property
center:false, // Centering on the screen?
filter:'none', // CSS Filter
dur: 2, // In beats (Topos unit)
rotation: ir(1, 360), // Rotation (in degrees)
posX: ir(1,1200), // CSS Horizontal Position
posY: ir(1, 800), // CSS Vertical Position
`,
false,
)}
## Canvas live coding
Documentation in progress! Copy the example and run it separately (Showing visualization examples in the documentation not implemented yet).
Canvas live coding is a feature that allows you to draw musical events to the canvas. Canvas can be used to create complex visualizations. The feature is based on the <a href="https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API" target="_blank">Canvas API</a> and the <a href="https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D" target="_blank">CanvasRenderingContext2D</a> interface. The feature is still in development and more functions will be added in the future.
In addition to the standard Canvas API, Topos also includes some pre-defined shapes for convenience. See the Shapes section below for more info.
* <ic>draw(f: Function)</ic> - Draws to a canvas with the given function.
${makeExample(
"Drawing to canvas",
`
beat(0.5) && clear() && draw(context => {
context.fillStyle = 'red';
// Begin the path for the heart shape
context.beginPath();
const x = wc();
const y = hc();
context.fillStyle = 'red';
// Begin the path for the heart shape
context.beginPath();
context.moveTo(x + 125, y + 50);
context.bezierCurveTo(x + 75, y, x, y + 75, x + 125, y + 175);
context.bezierCurveTo(x + 250, y + 75, x + 175, y, x + 125, y + 50);
// Fill the heart with red color
context.fill();
})
`,
false,
)}
${makeExample(
"Using draw with events and shapes",
`
beat(0.25) && sound("bass1:5").pitch(rI(1,6)).draw(x => {
donut(x.pitch)
}).out()
`,
false,
)}
${makeExample(
"Using draw with ziffers and shapes",
`
z1("1/8 (0 2 1 4)+(2 1)").sound("sine").ad(0.05,.25).clear()
.draw(x => {
pie({slices:7,eaten:(7-x.pitch-1),fillStyle:"green", rotate: 250})
}).log("pitch").out()
`,
false,
)}
* <ic<image(url, x, y, width, height, rotation)</ic> - Draws an image to a canvas.
${makeExample(
"Image to canvas",
`
beat(0.5) && clear() && image("http://localhost:8000/topos_frog.svg",200,200+epulse()%15)
`,
false,
)}
* <ic>clear()</ic> - Clears the canvas.
* <ic>background(fill: string)</ic> - Sets the background color, image or gradient.
* <ic>w()</ic> - Returns the canvas width.
* <ic>h()</ic> - Returns the canvas height.
* <ic>wc()</ic> - Returns the center of the canvas width.
* <ic>hc()</ic> - Returns the center of the canvas height.
### Text to canvas
Text can be drawn to canvas using the <ic>drawText()</ic> function. The function can take any unicode characters including emojis. The function can also be used to draw random characters from a given unicode range. Different filters can also be applied using the **filter** parameter. See filter in <a href="https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/filter" target="_blank">canvas documentation</a> for more info.
* <ic>drawText(text, fontSize, rotation, font, x, y)</ic> - Draws text to a canvas.
${makeExample(
"Writing to canvas",
`
beat(0.5) && clear() && drawText("Hello world!", 100, 0, "Arial", 100, 100)
`,
false,
)}
* <ic>randomChar(number, min, max)</ic> - Returns a number of random characters from given unicode range.
${makeExample(
"Drawing random characters to canvas",
`
beat(0.5) && clear() && drawText(randomChar(10,1000,2000),30)
`,
false,
)}
* <ic>emoji(size)</ic> - Returns a random emojis as text.
* <ic>animals(size)</ic> - Returns a random animal emojis as text.
* <ic>food(size)</ic> - Returns a random food emojis as text.
${makeExample(
"Drawing food emojis to canvas",
`
beat(0.5) && clear() && drawText({x: 10, y: epulse()%700, text: food(50)})
`,
false,
)}
* <ic>expression(size)</ic> - Returns a random expression emojis as text.
### Shapes
In addition to supporting drawing to canvas directly, Topos also include some pre-defined shapes for convenience. Every shape can be defined by either by inputting one object as parameter or by inputting the parameters separately.
The predefined shapes are:
* <ic>smiley(happiness, radius, eyes, fill, rotate, x, y)</ic>
* <ic>ball(radius,fill,x,y)</ic>
* <ic>box(width, height, fill, rotate)</ic>
* <ic>pointy(width, height, fill, rotate, x, y)</ic>
* <ic>equilateral(radius, fill, rotate, x, y)</ic>
* <ic>star(points, radius, fill rotate, outerRadius, x, y</ic>
* <ic>pie(slices, eaten, radius, fill, secondary, stroke, rotate, x, y</ic>
* <ic>donut(slices, eaten, radius, hole, fill, secondary, stroke, rotate, x, y</ic>
* <ic>balloid(petals, radius, curve, fill, secondary, x, y)</ic>
* <ic>stroke(width, stroke, rotate, x1, y1, x2, y2)</ic>
### Gradients
* <ic>linearGradient(x1, y1, x2, y2, ...stops)</ic> - Creates a <a href="https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/createLinearGradient">linear gradient</a>.
* <ic>radialGradient(x1, y1, r1, x2, y2, r2, ...stops)</ic> - Creates a <a href="https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/createRadialGradient">radial gradient</a>.
* <ic>conicGradient(x, y, angle, ...stops)</ic> - Creates a <a href="https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D/createConicGradient">conic gradient</a>.
`;
};

View File

@@ -8,7 +8,7 @@ export const generators = (application: Editor): string => {
JavaScript <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Generator" target="_blank">generators</a> are powerful functions for generating value sequences. They can be used to generate melodies, rhythms or control parameters.
In Topos generator functions should be called using the <ic>cache(key, function)</ic> function to store the current state of the generator. This function takes two arguments: the name for the cache and the generator instance.
In Topos generator functions should be called using the <ic>cache(key, function)</ic> function to store the current state of the generator. This function takes two arguments: the name for the cache and the generator instance.
Once the generator is cached the values will be returned from the named cache even if the generator function is modified. To clear the current cache and to re-evaluate the modified generator use the **Shift+Ctrl+Backspace** shortcut. Alternatively you can cache the modified generator using a different name.
@@ -38,14 +38,16 @@ ${makeExample(
const s = Math.tan(x/10)+Math.sin(x/20);
yield 2 * Math.pow(s, 3) - 6 * Math.pow(s, 2) + 5 * s + 200;
x++;
}
}
}
beat(.125) && sound("triangle").freq(cache("mathyshit",poly())).out()
`,
true,
)};
When you want to dance with a dynamical system in controlled musical chaos, Topos is waiting for you:
${makeExample(
"Truly scale free chaos inspired by Lorentz attractor",
`
@@ -54,16 +56,16 @@ ${makeExample(
const dx = 10 * (y - x);
const dy = x * (rho - z) - y;
const dz = x * y - beta * z;
x += dx * 0.01;
y += dy * 0.01;
z += dz * 0.01;
const value = 300 + 30 * (Math.sin(x) + Math.tan(y) + Math.cos(z))
yield value;
}
}
beat(0.25) :: sound("triangle")
.freq(cache("stranger",strange(3,5,2)))
.adsr(.15,.1,.1,.1)
@@ -72,9 +74,61 @@ ${makeExample(
true,
)};
${makeExample(
"Henon and his discrete music",
`
function* henonMap(x = 0, y = 0, a = 1.4, b = 0.3) {
while (true) {
const newX = 1 - a * x ** 2 + y;
const newY = b * x;
const fusionPoint = newX + newY
yield fusionPoint * 300;
[x, y] = [newX, newY]
}
}
beat(0.25) :: sound("sawtooth")
.semitones(1,1,2,2,2,1,2,1)
.freq(cache("Hénon Synth", henonMap()))
.adsr(0, 0.1, 0.1, 0.5).out()
z0('1 {-2}').octave(-2).sound('bd').out()
z1('e. 1 s 3!2 e 3!2 s 9 8 1')
.sound('dr').gain(0.3).octave(-5).out()
`,
true,
)};
${makeExample(
"1970s fractal dream",
`
function* rossler(x = 0.1, y = 0.1, z = 0.1, a = 0.2, b = 0.2, c = 5.7) {
while (true) {
const dx = - y - z;
const dy = x + (a * y);
const dz = b + (x * z) - (c * z);
x += dx * 0.01;
y += dy * 0.01;
z += dz * 0.01;
const value = 250 * (Math.cosh(x*z) + Math.sinh(y*z))
yield value % 120 + 100;
}
}
beat(0.25) :: sound("triangle")
.freq(cache("Rössler attractor", rossler(3,4,1)))
.adsr(0,.1,.1,.1)
.log("freq").out()
`,
true,
)};
## OEIS integer sequences
To find some inspiration - or to enter into the void - one can visit <a href="https://oeis.org/" target="_blank">The On-Line Encyclopedia of Integer Sequences (OEIS)</a> to find some interesting integer sequences.
To find some inspiration - or to enter into the void - one can visit <a href="https://oeis.org/" target="_blank">The On-Line Encyclopedia of Integer Sequences (OEIS)</a> to find some interesting integer sequences.
Many of the sequences are implemented by <a href="https://github.com/acerix/jisg/tree/main/src/oeis" target="_blank">JISG</a> (Javascript Integer Sequence Generators) project. Those sequences can be referenced directly with the identifiers using the cache function.
@@ -106,7 +160,7 @@ function* poly(x) {
x++;
}
}
z0(poly(1)).noteLength(0.5).semitones(2,2,3,2,2,2).sound("sine").out()
z1(poly(8)).noteLength(0.25).semitones(2,1,2,1,2,2).sound("sine").out()
z2(poly(-3)).noteLength(1.0).semitones(2,2,2,1,3,2).sound("sine").out()

View File

@@ -120,6 +120,19 @@ beat(1)::sound(['kick', 'fsnare'].dur(3, 1))
true,
)}
## Iterating over lists
- <ic>counter(name,limit?,step?)</ic>: return the next value on the list based on counter value. The limit is optional and defaults to the length of the list. The step is optional and defaults to 1. Setting / changing limit will reset the counter.
- <ic>$(name,limit?,step?)</ic>: shorter alias for the counter.
${makeExample(
"Using counter to iterate over a list",
`
beat(0.5) :: sound("bd").gain(line(0,1,0.01).$("ramp")).out()
`,
true,
)}
## Manipulating notes and scales
- <ic>pitch()</ic>: convert a list of integers to pitch classes
@@ -152,7 +165,7 @@ ${makeExample(
"Play pitches from scale created from cent intervals",
`
rhythm([0.5,0.25].beat(1),14,16) :: sound('pluck')
.stretch(r(1,5)).pitch(r(0,6)).key(57)
.stretch(iR(1,5)).pitch(iR(0,6)).key(57)
.cents(120,270,540,670,785,950,1215).out()
`,
true,

View File

@@ -54,6 +54,7 @@ By default chance operators will be evaluated 48 times within a beat. You can ch
- <ic>frequently(beats?: number)</ic>: returns true 90% of the time in given number of beats
- <ic>almostAlways(beats?: number)</ic>: returns true 99% of the time in given number of beats
- <ic>always(beats?: number)</ic>: returns true. Can be handy when switching between different probabilities
- <ic>once()</ic>: returns true once, then false until the code is force evaluated (Shift+Ctrl+Enter)
Examples:

View File

@@ -7,20 +7,15 @@ export const variables = (application: Editor): string => {
# 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.
By default, each script is independant from each other. The variables defined in **script 1** are not available in **script 2**, etc. Moreover, they are overriden everytime the file is evaluated. It means that you cannot store any state or share information. However, you can use global variables to make that possible.
**However**, everybody knows that global variables are cool and should be used everywhere. Global variables are an incredibely powerful tool to radically alter a composition in a few lines of code.
There is a <ic>global</ic> object that you can use to store and retrieve information. It is a simple key/value store. You can store any type of data in it:
- <ic>variable(a: number | string, b?: any)</ic>: 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.
- <ic>delete_variable(name: string)</ic>: deletes a global variable from storage.
- <ic>clear_variables()</ic>: clear **ALL** variables. **This is a destructive operation**!
**Note:** since this example is running in the documentation, we cannot take advantage of the multiple scripts paradigm. Try to send a variable from the global file to the local file n°6.
${makeExample(
"Setting a global variable",
`
v('my_cool_variable', 2)
// This is script n°3
global.my_variable = 2
`,
true,
)}
@@ -28,15 +23,16 @@ v('my_cool_variable', 2)
${makeExample(
"Getting that variable back and printing!",
`
// Note that we just use one argument
log(v('my_cool_variable'))
// This is script n°4
log(global.my_variable)
`,
false,
true,
)}
Now your scripts can share information with each other!
## 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 <ic>i</ic> 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.
- <ic>counter(name: number | string, limit?: number, step?: number)</ic>: reads the value of the counter <ic>name</ic>. You can also call this function using the dollar symbol: <ic>$</ic>.

View File

@@ -33,6 +33,8 @@ declare global {
gen(min: number, max: number, times: number): number[];
sometimes(func: Function): number[];
apply(func: Function): number[];
counter(name: string | number, limit?: number, step?: number): number[]
$(name: string | number, limit?: number, step?: number): number[];
}
}
@@ -398,8 +400,31 @@ export const makeArrayExtensions = (api: UserAPI) => {
return this[Math.floor(api.randomGen() * this.length)];
};
Array.prototype.rand = Array.prototype.random;
Array.prototype.counter = function(
name: string | number,
limit?: number,
step?: number) {
/**
* @param n - Returns next item in array until the end, then returns the last value.
*
* @returns the shifted array
*/
const idx = api.counter(name,limit,step);
if(limit) {
return this[idx % this.length];
} else if(idx < this.length) {
return this[idx];
} else {
return this[this.length - 1];
}
};
Array.prototype.$ = Array.prototype.counter;
};
Array.prototype.scale = function (
scale: string = "major",
base_note: number = 0,

View File

@@ -12,10 +12,10 @@ import {
Universe,
loadUniverserFromUrl,
} from "./FileManagement";
import { singleElements, buttonGroups, ElementMap } from "./DomElements";
import { singleElements, buttonGroups, ElementMap, createDocumentationStyle } from "./DomElements";
import { registerFillKeys, registerOnKeyDown } from "./KeyActions";
import { installEditor } from "./EditorSetup";
import { documentation_factory } from "./Documentation";
import { documentation_factory, documentation_pages, showDocumentation, updateDocumentationContent } from "./Documentation";
import { EditorView } from "codemirror";
import { Clock } from "./Clock";
import { loadSamples, UserAPI } from "./API";
@@ -31,13 +31,12 @@ import { makeStringExtensions } from "./extensions/StringExtensions";
import { installInterfaceLogic } from "./InterfaceLogic";
import { installWindowBehaviors } from "./WindowBehavior";
import { makeNumberExtensions } from "./extensions/NumberExtensions";
// @ts-ignore
import { registerSW } from "virtual:pwa-register";
import colors from "./colors.json";
// @ts-ignore
const images = import.meta.glob("./assets/*")
if ("serviceWorker" in navigator) {
registerSW();
}
export class Editor {
// Universes and settings
@@ -87,6 +86,8 @@ export class Editor {
mode: "scope",
size: 1,
};
bindings: any[] = [];
documentationStyle: any = {};
// UserAPI
api: UserAPI;
@@ -191,7 +192,7 @@ export class Editor {
// ================================================================================
registerFillKeys(this);
registerOnKeyDown(this);
registerOnKeyDown(this);
installInterfaceLogic(this);
scriptBlinkers();
@@ -219,6 +220,24 @@ export class Editor {
this.settings.theme = "Everblush";
this.readTheme(this.settings.theme);
}
this.documentationStyle = createDocumentationStyle(this);
this.bindings = Object.keys(this.documentationStyle).map((key) => ({
type: "output",
regex: new RegExp(`<${key}([^>]*)>`, "g"),
//@ts-ignore
replace: (match, p1) => `<${key} class="${this.documentationStyle[key]}" ${p1}>`,
}));
// Get documentation id from hash parameter
const document_id = window.location.hash.slice(1);
if(document_id && document_id !== "" && documentation_pages.includes(document_id)) {
this.currentDocumentationPane = document_id
updateDocumentationContent(this, this.bindings);
showDocumentation(this);
}
}
private getBuffer(type: string): any {

View File

@@ -4,15 +4,16 @@ import viteCompression from "vite-plugin-compression";
const vitePWAconfiguration = {
devOptions: {
enabled: true,
enabled: false,
suppressWarnings: true,
},
workbox: {
sourcemap: false,
cleanupOutdatedCaches: false,
maximumFileSizeToCacheInBytes: 10000000,
globPatterns: [
"**/*.{js,js.gz,css,html,gif,png,json,woff,woff2,json,ogg,wav,mp3,ico,png,svg}",
"favicon/*.{js,js.gz,css,html,gif,png,json,woff,woff2,json,ogg,wav,mp3,ico,png,svg}",
],
runtimeCaching: [
{
@@ -35,14 +36,9 @@ const vitePWAconfiguration = {
},
],
},
includeAssets: [
"favicon/favicon.icon",
"favicon/apple-touch-icon.png",
"mask-icon.svg",
],
manifest: "manifest.webmanifest",
manifest: false,
registerType: "autoUpdate",
injectRegister: "auto",
injectRegister: "script-defer",
};
export default defineConfig(({ command, mode, ssrBuild }) => {
@@ -54,6 +50,13 @@ export default defineConfig(({ command, mode, ssrBuild }) => {
port: 8000,
strictPort: true,
},
build: {
outDir: "dist",
emptyOutDir: true,
cssCodeSplit: true,
cssMinify: true,
minify: true,
}
};
} else {
return {

View File

@@ -2203,7 +2203,7 @@ fast-glob@^3.2.12:
merge2 "^1.3.0"
micromatch "^4.0.4"
fast-glob@^3.3.1:
fast-glob@^3.3.2:
version "3.3.2"
resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.2.tgz#a904501e57cfdd2ffcded45e99a54fef55e46129"
integrity sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==
@@ -3774,13 +3774,13 @@ vite-plugin-markdown@^2.1.0:
htmlparser2 "^6.0.0"
markdown-it "^12.0.0"
vite-plugin-pwa@^0.16.7:
version "0.16.7"
resolved "https://registry.yarnpkg.com/vite-plugin-pwa/-/vite-plugin-pwa-0.16.7.tgz#3dcacc342766ff3598472ac7d5e0782d14e2853e"
integrity sha512-4WMA5unuKlHs+koNoykeuCfTcqEGbiTRr8sVYUQMhc6tWxZpSRnv9Ojk4LKmqVhoPGHfBVCdGaMo8t9Qidkc1Q==
vite-plugin-pwa@^0.17.4:
version "0.17.4"
resolved "https://registry.yarnpkg.com/vite-plugin-pwa/-/vite-plugin-pwa-0.17.4.tgz#be3b3714d4148681bc73e8e0b1e6ce1a71fa79f2"
integrity sha512-j9iiyinFOYyof4Zk3Q+DtmYyDVBDAi6PuMGNGq6uGI0pw7E+LNm9e+nQ2ep9obMP/kjdWwzilqUrlfVRj9OobA==
dependencies:
debug "^4.3.4"
fast-glob "^3.3.1"
fast-glob "^3.3.2"
pretty-bytes "^6.1.1"
workbox-build "^7.0.0"
workbox-window "^7.0.0"