diff --git a/crates/forth/src/words/effects.rs b/crates/forth/src/words/effects.rs index df7a2b3..d933a20 100644 --- a/crates/forth/src/words/effects.rs +++ b/crates/forth/src/words/effects.rs @@ -789,4 +789,64 @@ pub(super) const WORDS: &[Word] = &[ compile: Param, varargs: false, }, + Word { + name: "feedback", + aliases: &["fb"], + category: "Mod FX", + stack: "(f --)", + desc: "Set feedback delay level", + example: "0.7 feedback", + compile: Param, + varargs: false, + }, + Word { + name: "fbtime", + aliases: &["fbt"], + category: "Mod FX", + stack: "(f --)", + desc: "Set feedback delay time in ms", + example: "30 fbtime", + compile: Param, + varargs: false, + }, + Word { + name: "fbdamp", + aliases: &["fbd"], + category: "Mod FX", + stack: "(f --)", + desc: "Set feedback delay damping", + example: "0.3 fbdamp", + compile: Param, + varargs: false, + }, + Word { + name: "fblfo", + aliases: &[], + category: "Mod FX", + stack: "(f --)", + desc: "Set feedback delay LFO rate in Hz", + example: "2 fblfo", + compile: Param, + varargs: false, + }, + Word { + name: "fblfodepth", + aliases: &[], + category: "Mod FX", + stack: "(f --)", + desc: "Set feedback delay LFO depth", + example: "0.5 fblfodepth", + compile: Param, + varargs: false, + }, + Word { + name: "fblfoshape", + aliases: &[], + category: "Mod FX", + stack: "(s --)", + desc: "Set feedback delay LFO shape", + example: "tri fblfoshape", + compile: Param, + varargs: false, + }, ]; diff --git a/website/public/Cagire.png b/website/public/Cagire.png new file mode 100644 index 0000000..f7a4b23 Binary files /dev/null and b/website/public/Cagire.png differ diff --git a/website/public/CozetteVector.ttf b/website/public/CozetteVector.ttf deleted file mode 100644 index ff273b7..0000000 Binary files a/website/public/CozetteVector.ttf and /dev/null differ diff --git a/website/public/SpaceMono-Bold.woff2 b/website/public/SpaceMono-Bold.woff2 new file mode 100644 index 0000000..6563bad Binary files /dev/null and b/website/public/SpaceMono-Bold.woff2 differ diff --git a/website/public/SpaceMono-Regular.woff2 b/website/public/SpaceMono-Regular.woff2 new file mode 100644 index 0000000..2752f60 Binary files /dev/null and b/website/public/SpaceMono-Regular.woff2 differ diff --git a/website/public/favicon.ico b/website/public/favicon.ico new file mode 100644 index 0000000..9b55471 Binary files /dev/null and b/website/public/favicon.ico differ diff --git a/website/public/script.js b/website/public/script.js index 4c87d9d..c303a45 100644 --- a/website/public/script.js +++ b/website/public/script.js @@ -14,65 +14,28 @@ toggle.addEventListener('click', () => { const light = root.classList.contains('light'); toggle.textContent = light ? 'DARK' : 'LIGHT'; localStorage.setItem('theme', light ? 'light' : 'dark'); - highlightForth(); }); -function highlightForth() { - const words = ['note', 'sound', 'lpf', 'hpf', 'chorus', 'verb', 'distort', 'speed']; - const notes = ['c4']; - const chords = ['min7']; - const samples = ['kkick', 'sine', 'saw']; - const isLight = document.documentElement.classList.contains('light'); - const numColor = isLight ? '#a855f7' : '#e8a0e8'; - const dotColor = isLight ? '#0284c7' : '#7dd3fc'; - const wordColor = isLight ? '#65a30d' : '#a3e635'; - const noteColor = isLight ? '#d97706' : '#fbbf24'; - const chordColor = isLight ? '#15803d' : '#4ade80'; - const sampleColor = isLight ? '#dc2626' : '#f87171'; - document.querySelectorAll('pre').forEach(pre => { - const text = pre.dataset.source || pre.textContent; - pre.dataset.source = text; - pre.innerHTML = text - .split(/(\s+)/) - .map(t => { - if (t === '.') return `.`; - if (/^-?\d+\.?\d*$/.test(t)) return `${t}`; - if (words.includes(t)) return `${t}`; - if (notes.includes(t)) return `${t}`; - if (chords.includes(t)) return `${t}`; - if (samples.includes(t)) return `${t}`; - return t; - }) - .join(''); - }); -} - -if (document.readyState === 'loading') { - document.addEventListener('DOMContentLoaded', highlightForth); -} else { - highlightForth(); -} - -const kofiModal = document.getElementById('kofi-modal'); -const kofiFrame = document.getElementById('kofi-frame'); - -document.querySelectorAll('.downloads-table a').forEach(link => { - link.addEventListener('click', () => { - if (sessionStorage.getItem('kofi-dismissed')) return; - kofiFrame.src = 'https://ko-fi.com/raphaelbubo/?hidefeed=true&widget=true&embed=true'; - kofiModal.showModal(); +document.querySelectorAll('.example-cell').forEach(cell => { + cell.addEventListener('click', () => { + const video = cell.querySelector('video'); + const wasExpanded = cell.classList.contains('expanded'); + document.querySelectorAll('.example-cell.expanded').forEach(c => { + c.classList.remove('expanded'); + c.querySelector('video').pause(); + }); + if (!wasExpanded) { + cell.classList.add('expanded'); + video.play(); + } }); }); -kofiModal.addEventListener('close', () => { - sessionStorage.setItem('kofi-dismissed', '1'); - kofiFrame.src = 'about:blank'; -}); - -document.getElementById('kofi-close').addEventListener('click', () => { - kofiModal.close(); -}); - -kofiModal.addEventListener('click', (e) => { - if (e.target === kofiModal) kofiModal.close(); +document.addEventListener('keydown', (e) => { + if (e.key === 'Escape') { + document.querySelectorAll('.example-cell.expanded').forEach(c => { + c.classList.remove('expanded'); + c.querySelector('video').pause(); + }); + } }); diff --git a/website/public/style.css b/website/public/style.css index 88ba560..dba4f59 100644 --- a/website/public/style.css +++ b/website/public/style.css @@ -1,6 +1,15 @@ @font-face { - font-family: 'CozetteVector'; - src: url('/CozetteVector.ttf') format('truetype'); + font-family: 'Space Mono'; + src: url('/SpaceMono-Regular.woff2') format('woff2'); + font-weight: 400; + font-display: swap; +} + +@font-face { + font-family: 'Space Mono'; + src: url('/SpaceMono-Bold.woff2') format('woff2'); + font-weight: 700; + font-display: swap; } :root { @@ -20,14 +29,28 @@ } body { - font-family: 'CozetteVector', monospace; + font-family: 'Space Mono', monospace; background: var(--bg); color: var(--text); max-width: 800px; margin: 0 auto; padding: 1rem; line-height: 1.3; - font-size: 0.9rem; + font-size: clamp(0.9rem, 0.75rem + 0.75vw, 1.15rem); +} + +header { + display: flex; + align-items: center; + gap: 0.75rem; + margin-bottom: 1.5rem; +} + +.icon { + width: 4rem; + height: 4rem; + image-rendering: pixelated; + flex-shrink: 0; } h1 { @@ -35,11 +58,18 @@ h1 { margin: 0; } +.subtitle { + color: var(--text-muted); + margin: 0; +} + h2 { color: var(--text); - margin-top: 1rem; - margin-bottom: 0.25rem; - font-size: 1rem; + margin-top: 2rem; + margin-bottom: 1rem; + font-size: 1.15rem; + padding-bottom: 0.5rem; + border-bottom: 1px solid var(--text-muted); } p { margin: 0.25rem 0; } @@ -59,11 +89,47 @@ ul { li { margin: 0.1rem 0; } -pre { +.examples-grid { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 0.5rem; + margin: 0.5rem 0 2rem; +} + +.example-cell { + aspect-ratio: 1; + overflow: hidden; background: var(--surface); - padding: 0.5rem; - overflow-x: auto; - margin: 0.25rem 0; + cursor: pointer; +} + +.example-cell video { + width: 100%; + height: 100%; + object-fit: cover; + display: block; +} + +.example-cell.expanded { + position: fixed; + top: 0; + left: 0; + width: 100vw; + height: 100vh; + z-index: 100; + background: rgba(0, 0, 0, 0.9); + aspect-ratio: auto; + display: flex; + align-items: center; + justify-content: center; +} + +.example-cell.expanded video { + width: auto; + height: auto; + max-width: 90vw; + max-height: 90vh; + object-fit: contain; } .downloads-table { @@ -91,69 +157,34 @@ pre { } .note { - color: var(--text-muted); + color: var(--text-dim); font-size: 0.8rem; - font-style: italic; + background: var(--surface); + padding: 0.5rem 0.75rem; + margin-top: 0.75rem; +} + +.note::before { + content: '→ '; } .note a { color: var(--text-muted); } -.support { - background: var(--surface); - padding: 0.5rem; - margin: 0.5rem 0; +.colophon { + margin-top: 3rem; + padding-top: 1rem; + border-top: 1px solid var(--text-muted); + color: var(--text-muted); +} + +.colophon a { color: var(--text-dim); } -.support a { - color: var(--text); -} - -#kofi-modal { - background: var(--bg); - color: var(--text); - border: 1px solid var(--text-muted); - border-radius: 0; - padding: 1rem; - max-width: 420px; - width: 90vw; -} - -#kofi-modal::backdrop { - background: rgba(0, 0, 0, 0.7); -} - -#kofi-modal p { - margin-bottom: 0.5rem; -} - -#kofi-frame { - border: none; - width: 100%; - height: 570px; -} - -#kofi-close { - font-family: 'CozetteVector', monospace; - background: none; - color: var(--text-muted); - border: none; - padding: 0; - cursor: pointer; - font-size: inherit; - text-decoration: underline; - display: block; - margin: 0.5rem auto 0; -} - -#kofi-close:hover { - color: var(--text); -} - #theme-toggle { - font-family: 'CozetteVector', monospace; + font-family: 'Space Mono', monospace; background: none; color: var(--text-muted); border: none; diff --git a/website/src/pages/index.astro b/website/src/pages/index.astro index 1f40799..d884bc2 100644 --- a/website/src/pages/index.astro +++ b/website/src/pages/index.astro @@ -18,19 +18,42 @@ + +
-
+ AGPL-3.0 · Raphaël Maurice Forment · 2026
+Cagire is free and open source. If you find it useful, consider supporting the project on Ko-fi.
+| Platform | Desktop | Terminal |
|---|---|---|
| macOS (Universal) | +.pkg | +|
| macOS (ARM) | .app | @@ -54,65 +77,27 @@|
All releases are available on GitHub. You can also compile the software yourself by getting it from Cargo!
-Cagire is a step sequencer where each step contains a Forth script instead of typical note data. When the sequencer reaches a step, it runs the associated script. Scripts can produce sound, trigger samples, apply effects, or do nothing at all. You are free to define what your scripts will do. Cagire includes a built-in audio engine called Doux. No external software is needed to make sound. It comes with oscillators, sample players, filters, reverb, delay, distortion, and more.
-A minimal script that plays a middle C note using a sine wave:
-c4 note sine sound .-
And now let's make it polyphonic and add different parameters per voice:
-c4 min7 note -sine sound -0.1 chorus -500 1000 1500 lpf -.-
Sawtooth wave with lowpass filter, chorus and reverb:
-saw sound 1200 lpf 0.2 chorus 0.8 verb .-
Pitched-down kick drum sample with distortion:
-kkick sound 1.5 distort 0.8 speed .-
Live coding is a technique where a programmer writes code in real-time, often in front of an audience. It can be used to create music, visual art, and other forms of media. Learn more at TOPLAP or livecoding.fr.
+Live coding is a technique where a programmer writes code in real-time in front of an audience. It is a way to experiment with code, to share things and thoughts openly, to express yourself through code. It can be technical, poetical, weird, preferably all at once. Live coding can be used to create music, visual art, and other forms of media. Live coding is an autotelic activity: doing it is its own reward. There are no errors, only fun. Learn more at TOPLAP or livecoding.fr.
-AGPL-3.0 License ·
+