Feat: website WIP and new words

This commit is contained in:
2026-02-06 16:19:09 +01:00
parent 9a769518f9
commit e0d338a030
9 changed files with 203 additions and 164 deletions

BIN
website/public/Cagire.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
website/public/favicon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 137 KiB

View File

@@ -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 `<span style="color:${dotColor}">.</span>`;
if (/^-?\d+\.?\d*$/.test(t)) return `<span style="color:${numColor}">${t}</span>`;
if (words.includes(t)) return `<span style="color:${wordColor}">${t}</span>`;
if (notes.includes(t)) return `<span style="color:${noteColor}">${t}</span>`;
if (chords.includes(t)) return `<span style="color:${chordColor}">${t}</span>`;
if (samples.includes(t)) return `<span style="color:${sampleColor}">${t}</span>`;
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();
});
}
});

View File

@@ -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;

View File

@@ -18,19 +18,42 @@
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="Cagire - Forth-based live coding sequencer">
<meta name="twitter:description" content="Forth-based live coding music sequencer">
<link rel="icon" href="/favicon.ico">
<meta property="og:image" content="/Cagire.png">
<link rel="stylesheet" href="/style.css">
</head>
<body>
<h1>CAGIRE: LIVE CODING IN FORTH</h1>
<header>
<img class="icon" src="/Cagire.png" alt="Cagire">
<div>
<h1>CAGIRE: LIVE CODING IN FORTH</h1>
<p class="subtitle">AGPL-3.0 · Raphaël Maurice Forment · 2026</p>
</div>
</header>
<p class="support">Cagire is free and open source. If you find it useful, consider <a href="https://ko-fi.com/raphaelbubo">supporting the project on Ko-fi</a>.</p>
<div class="examples-grid">
<div class="example-cell"><video src="" muted loop playsinline></video></div>
<div class="example-cell"><video src="" muted loop playsinline></video></div>
<div class="example-cell"><video src="" muted loop playsinline></video></div>
<div class="example-cell"><video src="" muted loop playsinline></video></div>
<div class="example-cell"><video src="" muted loop playsinline></video></div>
<div class="example-cell"><video src="" muted loop playsinline></video></div>
<div class="example-cell"><video src="" muted loop playsinline></video></div>
<div class="example-cell"><video src="" muted loop playsinline></video></div>
<div class="example-cell"><video src="" muted loop playsinline></video></div>
</div>
<h2>Download</h2>
<table class="downloads-table">
<tr>
<th>Platform</th>
<th>Desktop</th>
<th>Terminal</th>
</tr>
<tr>
<td>macOS (Universal)</td>
<td colspan="2"><a href="https://github.com/Bubobubobubobubo/cagire/releases/latest/download/cagire-macos-universal.pkg">.pkg</a></td>
</tr>
<tr>
<td>macOS (ARM)</td>
<td><a href="https://github.com/Bubobubobubobubo/cagire/releases/latest/download/cagire-macos-aarch64-desktop.app.zip">.app</a></td>
@@ -54,65 +77,27 @@
</table>
<p class="note">All releases are available on <a href="https://github.com/Bubobubobubobubo/cagire/releases/latest">GitHub</a>. You can also compile the software yourself by getting it from Cargo!</p>
<dialog id="kofi-modal">
<p>If you enjoy Cagire, consider supporting the project:</p>
<iframe id="kofi-frame" src="about:blank" title="Ko-fi donation widget"></iframe>
<button id="kofi-close">CLOSE</button>
</dialog>
<video src="/mono_cagire.mp4" autoplay muted loop playsinline></video>
<h2>About</h2>
<p>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 <a href="https://doux.livecoding.fr">Doux</a>. No external software is needed to make sound. It comes with oscillators, sample players, filters, reverb, delay, distortion, and more.</p>
<h2>Code Examples</h2>
<br>
<p>A minimal script that plays a middle C note using a sine wave:</p>
<pre>c4 note sine sound .</pre>
<br>
<p>And now let's make it polyphonic and add different parameters per voice:</p>
<pre>c4 min7 note
sine sound
0.1 chorus
500 1000 1500 lpf
.</pre>
<br>
<p>Sawtooth wave with lowpass filter, chorus and reverb:</p>
<pre>saw sound 1200 lpf 0.2 chorus 0.8 verb .</pre>
<br>
<p>Pitched-down kick drum sample with distortion:</p>
<pre>kkick sound 1.5 distort 0.8 speed .</pre>
<br>
<h2>Features</h2>
<ul>
<li>Robust synthesis engine: synthesizers, sampling, effects, live input, and more to discover.</li>
<li>Ableton Link: jam with your friends or include other software / hardware to your setup.</li>
<li>32 banks × 32 patterns × 128 steps per project: (~131.000 scripts per project).</li>
<li>Forth: objectively the coolest / minimal / hackable language to make music with!</li>
<li>Embedded dictionary and documentation!</li>
<li>Embedded dictionary and documentation! Learn while coding!</li>
</ul>
<h2>Live Coding</h2>
<p>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 <a href="https://toplap.org">TOPLAP</a> or <a href="https://livecoding.fr">livecoding.fr</a>.</p>
<p>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 <a href="https://toplap.org">TOPLAP</a> or <a href="https://livecoding.fr">livecoding.fr</a>.</p>
<h2>Credits</h2>
<ul>
<li><a href="https://raphaelforment.fr">BuboBubo</a> (Raphaël Maurice Forment).</li>
<li>See <a href="https://doux.livecoding.fr">Doux</a> for engine credits.</li>
</ul>
<h2>Links</h2>
<ul>
<li><a href="https://github.com/Bubobubobubobubo/cagire">GitHub</a></li>
<li><a href="https://ko-fi.com/raphaelbubo">Ko-fi</a></li>
</ul>
<p style="margin-top: 2rem; color: var(--text-muted);">AGPL-3.0 License · <button id="theme-toggle" aria-label="Toggle theme">LIGHT</button></p>
<p class="colophon">
<a href="https://raphaelforment.fr">BuboBubo</a> · Audio engine: <a href="https://doux.livecoding.fr">Doux</a> · <a href="https://github.com/Bubobubobubobubo/cagire">GitHub</a> · AGPL-3.0 · <button id="theme-toggle" aria-label="Toggle theme">LIGHT</button>
</p>
<script is:inline src="/script.js"></script>
</body>