Commit 4bcb27e6 by PLN (Algolia)

feat(parvagues): bio, credibility strip, featured video, track metadata

- Add BioSection (livecoding explainer + genre tags) after hero
- Add AsSeenAt strip (TOPLAP, Algorave GZ, Opal, Cookie, 38C3, 39C3)
- Featured Algorave GZ25 video with neon badge
- New releases: CosmicFest album + Ton Numéro single with covers
- Fix Bandcamp/Spotify/YouTube streaming embeds + privacy consent
- Fix social links: remove Bluesky, GitLab at nech.pl/Tidal
- New live events: 38C3 House of Tea, Chaos Music Club, 39C3 sets
- Track metadata with Tidal code ingredients for 3 pilot gigs
- Poster support on live subpages (Algorave GZ25 poster)
- Expandable track breakdowns with code snippets on subpages
parent 6253c752
const credentials = [
{ name: 'TOPLAP', detail: '20 Years', url: 'https://toplap.org/' },
{ name: 'Algorave', detail: 'GZ Lyon', url: 'https://algorave.com/' },
{ name: 'Opal Festival', url: 'https://www.opal-music.com/' },
{ name: 'Cookie Collective', url: 'https://www.cookie.paris/' },
{ name: '38C3', detail: 'House of Tea', url: 'https://soundcloud.com/parvagues/live-38c3-house-of-tea' },
{ name: '39C3', detail: 'House of Tea', url: 'https://soundcloud.com/parvagues/live-39c3' },
];
export default function AsSeenAt() {
return (
<div className="py-10 border-y border-white/[0.06]">
<div className="max-w-5xl mx-auto px-6">
<p
className="text-[10px] tracking-[0.25em] uppercase text-center mb-6"
style={{ color: 'var(--text-muted)' }}
>
As seen at
</p>
<div className="flex flex-wrap justify-center items-center gap-x-8 gap-y-4">
{credentials.map(({ name, detail, url }) => (
<a
key={name}
href={url}
target="_blank"
rel="noopener noreferrer"
className="group flex items-baseline gap-1.5 transition-colors duration-200"
>
<span
className="font-display font-bold text-sm md:text-base tracking-wide text-white/40 group-hover:text-[var(--neon-high)] transition-colors duration-200"
>
{name}
</span>
{detail && (
<span className="text-[10px] tracking-wider text-white/20 group-hover:text-white/40 transition-colors duration-200">
{detail}
</span>
)}
</a>
))}
</div>
</div>
</div>
);
}
export default function BioSection() {
return (
<section className="reveal py-16 md:py-24">
<div className="max-w-3xl mx-auto px-6 text-center">
<p
className="text-base md:text-lg leading-relaxed"
style={{ color: 'var(--text-primary)' }}
>
<strong className="font-display">ParVagues</strong> est un projet de{' '}
<em>livecoding</em> solo — musique électronique composée et jouée en
temps réel par le code. Algorave, techno, drum &amp; bass, breakbeat :
chaque set est unique, joué sur scène avec{' '}
<a
href="https://tidalcycles.org/"
target="_blank"
rel="noopener noreferrer"
className="underline underline-offset-2 decoration-[var(--neon-high)]/40 hover:decoration-[var(--neon-high)] transition-colors"
>
TidalCycles
</a>{' '}
+ MIDI.
</p>
<div className="flex flex-wrap justify-center gap-2 mt-8">
{['Algorave', 'Techno', 'DnB', 'Breakbeat', 'Livecoding'].map((tag) => (
<span
key={tag}
className="px-3 py-1 text-[11px] tracking-widest uppercase rounded-full border border-white/10 text-[var(--text-muted)]"
>
{tag}
</span>
))}
</div>
</div>
</section>
);
}
......@@ -3,8 +3,8 @@ import Link from 'next/link';
import Image from 'next/image';
import { Syne } from 'next/font/google';
import { useState, useEffect } from 'react';
import { FaEnvelope, FaInstagram, FaYoutube, FaGithub } from 'react-icons/fa';
import { SiBluesky, SiMastodon } from 'react-icons/si';
import { FaEnvelope, FaInstagram, FaYoutube, FaGitlab } from 'react-icons/fa';
import { SiMastodon } from 'react-icons/si';
const syne = Syne({
subsets: ['latin'],
......@@ -15,8 +15,7 @@ const syne = Syne({
const socials = [
{ href: 'https://instagram.com/parvagues.mp3', icon: FaInstagram, label: 'Instagram' },
{ href: 'https://bsky.app/profile/nech.pl', icon: SiBluesky, label: 'Bluesky' },
{ href: 'https://github.com/parvagues', icon: FaGithub, label: 'GitHub' },
{ href: 'http://nech.pl/Tidal', icon: FaGitlab, label: 'GitLab' },
{ href: 'https://youtube.com/@parvagues', icon: FaYoutube, label: 'YouTube' },
{ href: 'https://chaos.social/@PixelNoir', icon: SiMastodon, label: 'Mastodon' },
];
......
......@@ -15,6 +15,22 @@ function DeezerIcon({ className }) {
const albums = [
{
id: '2025_cosmicfest',
title: 'Live @ CosmicFest',
image: '/images/parvagues/albums/2025_cosmicfest/cover.jpg',
links: [
{ platform: 'Bandcamp', url: 'https://parvagues.bandcamp.com/album/live-cosmicfest' },
],
},
{
id: '2025_ton_numero',
title: 'Ton Numéro',
image: '/images/parvagues/albums/2025_ton_numero/cover.jpg',
links: [
{ platform: 'Bandcamp', url: 'https://parvagues.bandcamp.com/track/ton-num-ro' },
],
},
{
id: '2024_opal',
title: 'Livecoding (Opal Festival 2024)',
image: '/images/parvagues/albums/2024_opal/cover.jpg',
......@@ -55,7 +71,7 @@ const streamingPlatforms = [
label: 'Bandcamp',
icon: FaBandcamp,
color: '#1da0c3',
embedUrl: 'https://bandcamp.com/EmbeddedPlayer/album=3869867806/size=large/bgcol=333333/linkcol=a700d1/tracklist=false/transparent=true/',
embedUrl: 'https://bandcamp.com/EmbeddedPlayer/album=644862623/size=large/bgcol=0a0a0a/linkcol=a700d1/tracklist=true/transparent=true/',
embedHeight: 450,
profileUrl: 'https://parvagues.bandcamp.com/',
},
......@@ -67,16 +83,20 @@ const streamingPlatforms = [
embedUrl: 'https://open.spotify.com/embed/artist/0kznTQnx5QRhMwktmZboX4?utm_source=generator&theme=0',
embedHeight: 450,
profileUrl: 'https://open.spotify.com/artist/0kznTQnx5QRhMwktmZboX4',
isPrivacy: true,
privacyProvider: 'Spotify',
},
{
id: 'youtube',
label: 'YouTube',
icon: FaYoutube,
color: '#ff0000',
embedUrl: 'https://www.youtube.com/embed?listType=user_uploads&list=@parvagues',
embedUrl: 'https://www.youtube-nocookie.com/embed/videoseries?list=OLAK5uy_l4MF3OCIXcdPMpsHGVX2Q9MiX6oU1zT6g',
embedHeight: 400,
profileUrl: 'https://www.youtube.com/@parvagues/videos',
isVideo: true,
isPrivacy: true,
privacyProvider: 'YouTube (Google)',
},
{
id: 'deezer',
......@@ -131,12 +151,17 @@ function AlbumCard({ album }) {
}
function StreamingEmbed({ platform, onClose }) {
if (platform.isPrivacy) {
const [consent, setConsent] = useState(false);
const Icon = platform.icon;
const providerName = platform.privacyProvider || platform.label;
// Privacy-only platforms (no embed, just redirect)
if (platform.isPrivacy && !platform.embedUrl) {
return (
<div className="bg-white/[0.03] border border-white/[0.06] rounded-xl p-12 text-center">
<FaInstagram className="w-10 h-10 text-[var(--text-muted)] mx-auto mb-4" />
<Icon className="w-10 h-10 text-[var(--text-muted)] mx-auto mb-4" />
<p className="text-sm text-[var(--text-muted)] mb-6 max-w-sm mx-auto">
Le contenu Instagram se connecte aux serveurs de Meta et peut suivre votre activité.
Le contenu {providerName} se connecte à des serveurs tiers et peut suivre votre activité.
</p>
<a
href={platform.profileUrl}
......@@ -144,12 +169,41 @@ function StreamingEmbed({ platform, onClose }) {
rel="noopener noreferrer"
className="inline-flex items-center gap-2 px-6 py-3 text-sm font-display font-semibold tracking-wider rounded-full border border-white/20 hover:bg-white hover:text-[var(--surface)] transition-all duration-300"
>
Voir sur Instagram
Voir sur {platform.label}
</a>
</div>
);
}
// Privacy consent gate (has embed but needs user approval)
if (platform.isPrivacy && !consent) {
return (
<div className="bg-white/[0.03] border border-white/[0.06] rounded-xl p-12 text-center">
<Icon className="w-10 h-10 text-[var(--text-muted)] mx-auto mb-4" />
<p className="text-sm text-[var(--text-muted)] mb-6 max-w-sm mx-auto">
Le contenu {providerName} se connecte à des serveurs tiers et peut suivre votre activité.
</p>
<div className="flex flex-wrap justify-center gap-4">
<button
onClick={() => setConsent(true)}
className="inline-flex items-center gap-2 px-6 py-3 text-sm font-display font-semibold tracking-wider rounded-full border border-white/20 hover:bg-white hover:text-[var(--surface)] transition-all duration-300"
>
Charger le lecteur
</button>
<a
href={platform.profileUrl}
target="_blank"
rel="noopener noreferrer"
className="inline-flex items-center gap-2 px-6 py-3 text-sm font-display tracking-wider rounded-full text-[var(--text-muted)] hover:text-white transition-all duration-300"
>
Voir sur {platform.label}
</a>
</div>
</div>
);
}
// Embed loaded (either no privacy gate, or consent given)
return (
<div className="bg-white/[0.03] border border-white/[0.06] rounded-xl overflow-hidden">
<div style={{ height: platform.isVideo ? undefined : platform.embedHeight }}>
......
import { FaInstagram, FaTwitter, FaGithub } from 'react-icons/fa';
import { SiBluesky, SiMastodon } from 'react-icons/si';
import { FaInstagram, FaGitlab } from 'react-icons/fa';
export default function SocialCTA() {
return (
<div className="flex flex-wrap justify-center gap-6 py-12">
<a
href="https://instagram.com/parvagues"
href="https://instagram.com/parvagues.mp3"
target="_blank"
rel="noopener noreferrer"
className="flex items-center gap-3 bg-gradient-to-br from-purple-600 to-pink-600 px-8 py-4 rounded-full text-white font-bold text-lg hover:from-purple-500 hover:to-pink-500 transition-all transform hover:scale-105 shadow-lg hover:shadow-purple-500/50"
......@@ -15,23 +14,13 @@ export default function SocialCTA() {
</a>
<a
href="https://bsky.app/profile/nech.pl"
target="_blank"
rel="noopener noreferrer"
className="flex items-center gap-3 bg-gray-800 hover:bg-gray-700 px-8 py-4 rounded-full text-white font-bold text-lg transition-all transform hover:scale-105 shadow-lg border border-white/10 hover:border-white/30"
>
<SiBluesky className="text-2xl" />
<span>Bluesky</span>
</a>
<a
href="https://github.com/parvagues"
href="http://nech.pl/Tidal"
target="_blank"
rel="noopener noreferrer"
className="flex items-center gap-3 bg-black hover:bg-gray-900 px-8 py-4 rounded-full text-white font-bold text-lg transition-all transform hover:scale-105 shadow-lg border border-white/30 hover:border-white"
>
<FaGithub className="text-2xl" />
<span>GitHub</span>
<FaGitlab className="text-2xl" />
<span>GitLab</span>
</a>
</div>
);
......
......@@ -4,14 +4,16 @@ import { FaPlay, FaYoutube } from 'react-icons/fa';
// source: 'archive' (default) | 'youtube'
// For YouTube: id is the YouTube video ID
// For archive.org: id is the archive identifier
const videos = [
{
const featuredVideo = {
id: 'bS5jcIJfU6c',
source: 'youtube',
title: 'Algorave GZ25',
subtitle: 'w/ Pérégrine · GZ Algorave',
subtitle: 'w/ Pérégrine · Algorave Lyon — European-wide annual event',
date: 'Fév 2025',
},
featured: true,
};
const videos = [
{
id: 'toplap-fromscratch-dec2025-parvagues',
title: 'From Scratch: Jungle 🐅',
......@@ -169,6 +171,16 @@ export default function VideoSection() {
</h2>
<div className="h-px bg-white/10 mt-4 mb-12" />
{/* Featured video — full width */}
<div className="mb-12">
<div className="relative">
<span className="absolute -top-3 left-4 z-10 px-3 py-0.5 text-[10px] tracking-[0.2em] uppercase font-display font-bold rounded-full bg-[var(--neon-high)] text-white">
Featured
</span>
<VideoCard video={featuredVideo} />
</div>
</div>
<div className="grid sm:grid-cols-2 gap-8 md:gap-10">
{videos.map((video) => (
<VideoCard key={video.id} video={video} />
......
---
title: "[38C3] Chaos Music Club"
date: "2024-12-29"
time: "23:00"
location: "Hamburg, Germany"
address: "CCH, Hamburg"
description: "Livecoding set at the Chaos Music Club, 38C3 Chaos Communication Congress."
ctaURL: "https://soundcloud.com/parvagues/live-38c3-chaos-music-club"
ctaText: "Écouter le set"
video: ""
audio: "https://soundcloud.com/parvagues/live-38c3-chaos-music-club"
archive: ""
tags: ["livecoding", "ccc", "38c3", "chaos-music-club", "live"]
---
Livecoding set at the Chaos Music Club, 38C3 edition of the Chaos Communication Congress.
---
title: "[38C3] House of Tea"
date: "2024-12-29"
time: "22:00"
location: "Hamburg, Germany"
address: "CCH, Hamburg"
description: "Livecoding set at the House of Tea, 38C3 Chaos Communication Congress."
ctaURL: "https://soundcloud.com/parvagues/live-38c3-house-of-tea"
ctaText: "Écouter le set"
video: ""
audio: "https://soundcloud.com/parvagues/live-38c3-house-of-tea"
archive: ""
tags: ["livecoding", "ccc", "38c3", "house-of-tea", "live"]
---
Livecoding set at the House of Tea, 38C3 edition of the Chaos Communication Congress.
{
"gig": "38c3-toilet",
"title": "[38C3] Secret Toilet Rave",
"date": "2024-12-28",
"bpmRange": [124, 170],
"styleDistribution": {
"acid-techno": 2,
"punk-dnb": 2,
"downtempo": 1
},
"samplePacks": [
{ "name": "bassWarsaw", "usage": "primary bass synth in Ghosts and L'or Bleu" },
{ "name": "ghost", "usage": "namesake haunted samples cycled through 4 variations" },
{ "name": "rhadamanthe_vocal", "usage": "'self-destruct sequence' vocal drops in Ghosts" },
{ "name": "breaks165 / jungle_breaks", "usage": "breakbeat backbone across multiple tracks" },
{ "name": "vec1_acid / vec2_synth_acid", "usage": "triple-layered acid bass in Acidulé" },
{ "name": "FMRhodes1", "usage": "tremolo piano lead in Acidulé" },
{ "name": "punk", "usage": "punk guitar chops in Nouveau Punk" },
{ "name": "meth_bass", "usage": "dub-delay bass slices in Pitbul Punk" },
{ "name": "moog", "usage": "chromatic lead in Pitbul Punk" },
{ "name": "cpluck", "usage": "'Violoncelle Manouche punko-magique' in Nouveau Punk" },
{ "name": "90s_synatm", "usage": "atmospheric pads with triple destruction bus" },
{ "name": "suns_keys / suns_guitar", "usage": "Leifur James remix samples in L'or Bleu" }
],
"tracks": [
{
"name": "Ghosts in the Toilets",
"file": "live/collab/ccc/ghosts_in_the_toilets.tidal",
"bpm": 160,
"style": "dnb",
"ingredients": [
{ "type": "structure", "code": "setcps (160/60/4)", "description": "160 BPM, fast DNB-adjacent tempo" },
{ "type": "sample", "code": "\"ghost\" # n (slow 4 $ \"<0 1 2 3>\")", "description": "Ghost samples cycling through 4 variations — the track's namesake" },
{ "type": "technique", "code": "slice 16 \"[0 .. 7] . <[0 .. 7]!3 [0 1 . [2 3]]>\"", "description": "Breakbeat slicer on breaks165 with variable resequencing" },
{ "type": "effect", "code": "# octersubsub 2 # octersub 2 # sz 0.01 # room 1.0", "description": "Heavy octave sub-bass doubling with tight reverb" },
{ "type": "pattern", "code": "note (\"<e3 fs3 <gs3 d4> <a3 df4>>\" - 12) # \"bassWarsaw\"", "description": "Dark Warsaw bass with diminished/augmented interval cycle" }
]
},
{
"name": "Nouveau Punk",
"file": "live/collab/raph/nouveau_punk.tidal",
"bpm": 155,
"style": "punk-dnb",
"ingredients": [
{ "type": "structure", "code": "setcps (155/60/4)", "description": "155 BPM — punk energy territory" },
{ "type": "sample", "code": "chop 4 $ \"punk:0/4\" # crushbus 41 (range 16 1.5 \"^53\")", "description": "Punk guitar chopped with MIDI-controlled bitcrush" },
{ "type": "pattern", "code": "note \"<e g a <b4 b4>>\" # \"meth_bass:3\" # hpf 2000", "description": "High-passed meth_bass following E-G-A-B progression" },
{ "type": "technique", "code": "struct \"t(<3!3 5>,8,<0!7 1>)\" $ arp \"up\" ... # \"cpluck:2\"", "description": "Euclidean-rhythmed arpeggiated 'Violoncelle Manouche punko-magique'" },
{ "type": "effect", "code": "# crushbus 52 # octersubbus 51 # octerbus 53", "description": "Triple MIDI-controlled destruction on 90s_synatm synth" }
]
},
{
"name": "Pitbul Punk",
"file": "live/collab/raph/punkachien.tidal",
"bpm": 170,
"style": "punk-dnb",
"ingredients": [
{ "type": "structure", "code": "setcps (170/60/4)", "description": "170 BPM — full DNB speed, fastest track" },
{ "type": "pattern", "code": "note (\"d*<3 4 3 2> . ds ds df\" + 2) # \"vec1_acid:11\"", "description": "Acid bassline with variable-density note patterns" },
{ "type": "technique", "code": "superimpose ((|* gain \"^17\") . (# n 10) . (# legato 0.8))", "description": "Layered acid superimposition with MIDI gain crossfade" },
{ "type": "sample", "code": "loopAt 2 $ slice 2 $ \"meth_bass:9\" # delay ... # delayfb ...", "description": "Meth bass with building dub-delay wash (0 to 0.8 feedback)" },
{ "type": "pattern", "code": "note \"<a as <a a df5 df5> <d df>>\" # \"moog\"", "description": "Moog lead with chromatic A-A#-A/Db-D progression" }
]
},
{
"name": "Acidulé",
"file": "live/collab/raph/acidule.tidal",
"bpm": 135,
"style": "acid-techno",
"ingredients": [
{ "type": "structure", "code": "setcps (135/60/4)", "description": "135 BPM acid techno groove" },
{ "type": "pattern", "code": "n \"<30!16 31!16>\" # \"vec2_synth_acid\"", "description": "Triple-layered acid bass stack: vec2 alternating + two vec1 lines" },
{ "type": "technique", "code": ">| note (\"[b d6 b cs6 d6 b cs6 b]*2\" - \"<0!32 12!32>\")", "description": "Acid sequence with octave drop every 32 cycles" },
{ "type": "sample", "code": "note (\"b ~ ~ b ~ ~ <d!7 e6> <~ d>\") # \"FMRhodes1\" # tremdp 4 # tremr 1.8", "description": "FM Rhodes 'Piano lead acidulé' with tremolo droplet texture" },
{ "type": "pattern", "code": "note (\"<b5!3 <g d>>@7 <c6 c6 cs6 a6>\") # \"moog:0\"", "description": "Caring bass — Moog with chromatic C-C#-A movement" }
]
},
{
"name": "L'or Bleu",
"file": "live/collab/mousquetaires/blue_gold.tidal",
"bpm": 124,
"style": "downtempo",
"ingredients": [
{ "type": "structure", "code": "setcps(124/60/4)", "description": "124 BPM — slowest, most melodic track" },
{ "type": "sample", "code": "\"suns_keys\" # n \"<0 <1 2> 3 3>\"", "description": "Suns of Gold keyboard stabs with tremolo and euclidean slicing" },
{ "type": "technique", "code": "fast (4/3) $ note (\"<[fs4 <a5!2 gs!4 fs5!4 ...>@3]*4>\")", "description": "Polymetric (4/3) Warsaw bass — triplets against 4/4 grid" },
{ "type": "sample", "code": "slice 8 \"<0 1 2 3>(3,8)\" $ \"suns_guitar:2\" # room 0.8", "description": "Euclidean-sliced guitar with lush reverb and L/R panning" },
{ "type": "effect", "code": "# crushbus 73 (range 16 2.9 \"^55\") # lpfbus 72", "description": "'CRASH/BURN THIS GUITAR' — MIDI-driven total destruction" }
]
}
]
}
{
"gig": "ccc-live",
"title": "CCC LIVE - Cookie Collective",
"date": "2024-10-01",
"bpmRange": [120, 160],
"styleDistribution": {
"nu-jazz": 3,
"dnb": 2,
"acid-techno": 1,
"breakbeat": 1,
"hybrid": 2
},
"samplePacks": [
{ "name": "nujazz_bass120", "usage": "bass foundation for nu-jazz tracks (Cafe Glace, Salut Nu)" },
{ "name": "nujazz_bass125", "usage": "bass foundation for Cafe Tiede and Force Motrice" },
{ "name": "nujazz_keys120/125", "usage": "jazz chord progressions across 4 tracks" },
{ "name": "jungle_breaks", "usage": "breakbeat layers in Cafe Glace (#22, #79, #19)" },
{ "name": "vec1_acid / vec2_synth_acid", "usage": "acid bass stack in L'ACID d'abord" },
{ "name": "suns_keys / suns_guitar", "usage": "Leifur James remix samples in Blue Gold" },
{ "name": "FMRhodes1/2", "usage": "FM synthesis piano, used in 3+ tracks" },
{ "name": "bassWarsaw", "usage": "primary bass synth across multiple tracks" },
{ "name": "fbass", "usage": "chopped bass in Alerte Verte" },
{ "name": "bass_commodore", "usage": "granular bass in Nuit Agitee" },
{ "name": "movie_vitality", "usage": "'I WANT TO GO TO MARS' vocal in Nass Revient" },
{ "name": "giorgio_syn", "usage": "Moroder tribute synth in Force Motrice" },
{ "name": "cbow", "usage": "cello bow with stutter delay in Force Motrice" },
{ "name": "meth_bass", "usage": "glitch bass layer in Cafe Glace" }
],
"tracks": [
{
"name": "L'ACID d'abord <3",
"file": "live/collab/raph/acidule.tidal",
"bpm": 135,
"style": "acid-techno",
"ingredients": [
{ "type": "structure", "code": "setcps (135/60/4)", "description": "135 BPM, acid techno tempo" },
{ "type": "sample", "code": "n \"<30!16 31!16>\" # \"vec2_synth_acid\"", "description": "Layered acid synth voices from vec2 bank, alternating presets every 16 cycles" },
{ "type": "sample", "code": "\"vec1_acid:40\" # hpf 200 # gain (range 0.6 1.1 \"^53\")", "description": "Secondary acid layer with high-pass filter, MIDI-controlled gain crossfade" },
{ "type": "technique", "code": ">| note (\"[b d6 b cs6 d6 b cs6 b]*2\" - \"<0!32 12!32>\")", "description": "Overriding note pattern with octave drop alternance every 32 cycles" },
{ "type": "effect", "code": "# tremdp 4 # tremr 1.8", "description": "Tremolo on the FM Rhodes lead creating a bouncy droplet texture" }
]
},
{
"name": "Alerte Verte",
"file": "live/midi/nova/dnb/alerte_verte.tidal",
"bpm": 160,
"style": "dnb",
"ingredients": [
{ "type": "structure", "code": "setcps (160/60/4)", "description": "160 BPM drum & bass tempo" },
{ "type": "sample", "code": "$ \"fbass:25\"", "description": "Chopped bass sample sliced into 16 parts via loopAt 4" },
{ "type": "technique", "code": "$ whenmod 16 12 (struct \"t*<4 4 8 16>\")", "description": "Snare rush building from quarter to 16th notes every 16 cycles" },
{ "type": "effect", "code": "# leslie \"^34\" # lsize (1 + (slow 16 perlin * 2)) # lrate 6.7", "description": "MIDI-controlled Leslie speaker with perlin-modulated cabinet size" },
{ "type": "sample", "code": "$ \"[808lc,<~!4 808mc!4>]\" # speed (range 0 2 \"^55\")", "description": "808 clave pattern with MIDI-pitchable speed" }
]
},
{
"name": "Blue Gold",
"file": "live/collab/ccc/ccc0.tidal",
"bpm": 140,
"style": "hybrid",
"ingredients": [
{ "type": "structure", "code": "setcps(140/60/4)", "description": "140 BPM, faster CCC live version of the Leifur James remix" },
{ "type": "sample", "code": "$ \"suns_keys\" # n \"<0 <1 2> 3 3>\"", "description": "Sampled keys with slice manipulation (8 slices, euclidean patterns)" },
{ "type": "technique", "code": "$ fast (4/3) $ note (\"<[fs4 <a5!2 gs!4 fs5!4 ...>@3]*4>\")", "description": "Triplet-feel bassline (4/3 speed) with complex ascending/descending notes" },
{ "type": "sample", "code": "$ \"suns_guitar:0/2\" # octersubbus 71", "description": "Sampled guitar with MIDI-controlled sub-octave destruction" },
{ "type": "effect", "code": "# crushbus 73 (range 16 2.9 \"^55\")", "description": "MIDI-controlled bitcrush from clean 16-bit to destroyed 2.9-bit" }
]
},
{
"name": "Nuit Agitee",
"file": "live/midi/nova/breaks/nuit_agitee.tidal",
"bpm": 160,
"style": "breakbeat",
"ingredients": [
{ "type": "structure", "code": "setcps (160/60/4)", "description": "160 BPM breakbeat tempo" },
{ "type": "technique", "code": "let scale = getScale (scaleTable ++ [(\"gameboy\", [1,2,4,5,7,9,11])])", "description": "Custom 'gameboy' scale definition for melodic content" },
{ "type": "sample", "code": "$ loopAt 8 $ chop 64 $ \"bass_commodore:7\"", "description": "Commodore bass chopped into 64 slices — granular bass texture" },
{ "type": "effect", "code": "# crushbus 41 # octersubbus 42 # lesliebus 43", "description": "Triple simultaneous bus effects on bass, all MIDI-controlled" },
{ "type": "sample", "code": "# \"harpsichord2\" # distortbus 51", "description": "Harpsichord lead using gameboy scale with MIDI distortion" }
]
},
{
"name": "Nass Revient de Mars!",
"file": "live/dnb/nass_revient.tidal",
"bpm": 140,
"style": "hybrid",
"ingredients": [
{ "type": "structure", "code": "setcps (140/60/4)", "description": "140 BPM hybrid techno/DNB" },
{ "type": "technique", "code": "$ superimpose (|+| note \"~ 7(<3!3 5>,8)\")", "description": "Supersaw bassline with euclidean 5th-interval superimposition" },
{ "type": "sample", "code": "$ \"forgan:24\" # crushbus 5", "description": "Massive organ sample with perlin-noise-driven bitcrush" },
{ "type": "technique", "code": "arp \"up\" \"<[c'maj'4 c'maj'3]!12 ...>\"", "description": "FM Rhodes arpeggios using chord inversions" },
{ "type": "sample", "code": "$ \"movie_vitality:10\"", "description": "'I WANT TO GO TO MARS' — movie sample with expanding window" }
]
},
{
"name": "Atelier de Force Motrice",
"file": "live/midi/nova/dnb/force_motrice.tidal",
"bpm": 125,
"style": "nu-jazz",
"ingredients": [
{ "type": "structure", "code": "setcps (125/60/4)", "description": "125 BPM, nu-jazz/breaks territory" },
{ "type": "sample", "code": "$ chop 16 $ loopAt 4 $ \"nujazz_bass125:1\"", "description": "Nu-jazz bass chopped into 16 slices with MIDI crush and octave" },
{ "type": "technique", "code": "# modIndex (range 0 2 \"^34\") # \"FMRhodes1\"", "description": "FM Rhodes with MIDI-controlled FM synthesis depth" },
{ "type": "sample", "code": "$ \"giorgio_syn:22\"", "description": "Giorgio synth (Moroder tribute) with crushable bus effect" },
{ "type": "effect", "code": "$ stut 4 0.65 \"e\" $ note ... # \"cbow:2\"", "description": "Cello bow with 4-step stutter delay creating ghostly trails" }
]
},
{
"name": "Cafe Tiede",
"file": "live/midi/nova/nujazz/cafe_tiede.tidal",
"bpm": 125,
"style": "nu-jazz",
"ingredients": [
{ "type": "structure", "code": "setcps(125/60/4)", "description": "125 BPM nu-jazz groove" },
{ "type": "sample", "code": "$ chop 8 $ loopAt 4 $ \"nujazz_bass125:1\"", "description": "Nu-jazz bass with 4-bar loop chopped into 8 slices" },
{ "type": "sample", "code": "$ chop 8 $ loopAt 2 $ \"nujazz_keys125\" # n \"<56 56 57 ...>\"", "description": "Nu-jazz keys with 9 preset rotations creating chord progressions" },
{ "type": "effect", "code": "# roombus 110 (slow 16 $ range 0 0.9 saw)", "description": "Slowly building reverb over 16 cycles combined with sine LPF sweep" },
{ "type": "technique", "code": "$ slice 4 \"<<[0 1 2 3] [0 1 0 1 2 3 2 3]> ...>\"", "description": "Keys with alternating simple/doubled slice patterns" }
]
},
{
"name": "Cafe Glace",
"file": "live/midi/nova/nujazz/cafe_glace.tidal",
"bpm": 120,
"style": "nu-jazz",
"ingredients": [
{ "type": "structure", "code": "setcps(120/60/4)", "description": "120 BPM, cooler than Cafe Tiede" },
{ "type": "sample", "code": "$ \"nujazz_bass120:4\"", "description": "Nu-jazz bass at 120 BPM with MIDI-controlled bitcrush" },
{ "type": "technique", "code": "$ midiOn \"^58\" ((# freeze 0.2))", "description": "Spectral freeze effect triggered by MIDI on keys" },
{ "type": "sample", "code": "$ \"meth_bass\" # crush 8 # lpf (slow 32 $ range 4000 400 sine)", "description": "'UN GLITCH DANS MON CAFE!!?!' — glitch bass with closing filter" },
{ "type": "technique", "code": "$ midiOn \"^55\" (loopAt 1 . (|> \"jungle_breaks:19\"))", "description": "MIDI-switchable jungle break layers with variable slice density" }
]
},
{
"name": "Salut Nu",
"file": "live/midi/nova/nujazz/salut_nu.tidal",
"bpm": 120,
"style": "nu-jazz",
"ingredients": [
{ "type": "structure", "code": "setcps(120/60/4)", "description": "120 BPM nu-jazz encore" },
{ "type": "technique", "code": "juxBy 0.9 (mask ... . rev . (|+ note 12) . (|+ pan 0.2))", "description": "MIDI-triggered stereo mirror: reversed, octave-up, panned piano reflection" },
{ "type": "sample", "code": "$ slice 4 ... $ \"nujazz_keys120\" # n \"<54!16 53!16>\"", "description": "Piano with 16-step slice choreography between two presets" },
{ "type": "sample", "code": "note ... # \"bassWarsaw\" # octersub 0.5 # squizbus 42", "description": "Warsaw bass with sub-octave and MIDI-controlled squiz distortion" },
{ "type": "technique", "code": "$ jux rev $ note ... # \"moogBass\" # chorus 0.2 # room 0.8", "description": "'La chorale du jugement' — Moog bass with stereo call-and-response" }
]
}
]
}
---
title: "[39C3] House of Tea"
date: "2025-12-28"
time: "22:00"
location: "Hamburg, Germany"
address: "CCH, Hamburg"
description: "Livecoding set at the House of Tea, 39C3 Chaos Communication Congress."
ctaURL: "https://soundcloud.com/parvagues/live-39c3"
ctaText: "Écouter le set"
video: ""
audio: "https://soundcloud.com/parvagues/live-39c3"
archive: ""
tags: ["livecoding", "ccc", "39c3", "house-of-tea", "live"]
---
Livecoding set at the House of Tea, 39C3 edition of the Chaos Communication Congress.
---
title: "[39C3] Toilet Rave"
date: "2025-12-29"
time: "01:00"
location: "Hamburg, Germany"
address: "CCH, Hamburg"
description: "Livecoding set at the Toilet Rave, 39C3 Chaos Communication Congress."
ctaURL: "https://soundcloud.com/parvagues/39c3_toiletrave_master"
ctaText: "Écouter le set"
video: ""
audio: "https://soundcloud.com/parvagues/39c3_toiletrave_master"
archive: ""
tags: ["livecoding", "ccc", "39c3", "toilet-rave", "live"]
---
Livecoding set at the Toilet Rave, 39C3 edition of the Chaos Communication Congress.
---
title: "AlgoRave Lyon 2025"
date: "2025-05-25"
time: "XX:XX"
location: "GR TBD"
date: "2025-05-24"
time: "18:00"
location: "Vaulx-en-Velin, France"
address: " GRRRND ZERO VAULX, 56 - 60 Avenue Bohlen, Vaulx-en-Velin"
description: "AlgoRave 18h-06h avec musique installations et performances audiovisuelles."
ctaURL: "https://www.grrrndzero.org/index.php/2672-sam-24-05-algorave"
......@@ -36,8 +36,9 @@ teasing3: |
Tonight @ Lyon
video: ""
audio: ""
audio: "https://parvagues.bandcamp.com/track/live-algorave-ground-zero-2025"
archive: ""
poster: "/images/parvagues/lives/2025/algorave-lyon/poster.jpg"
tags: ["livecoding", "algorave", "lyon", "tidal"]
---
......
---
title: "CosmicFest v0"
date: "2025-06-21"
time: "20:00"
location: "Biarritz region, France"
address: ""
description: "Live set at CosmicFest v0."
ctaURL: "https://me.nech.pl/cosmicfest"
ctaText: "Voir l'event"
video: ""
audio: "https://parvagues.bandcamp.com/album/live-cosmicfest"
archive: ""
tags: ["livecoding", "cosmicfest", "live"]
---
Live set at CosmicFest v0.
{
"gig": "cosmicfest",
"title": "Live @ CosmicFest v0",
"date": "2025-06-21",
"bpmRange": [80, 160],
"styleDistribution": {
"ambient": 1,
"lounge": 3,
"breaks": 3,
"techno": 2,
"dnb": 2,
"acid": 1,
"drill": 1,
"collab": 1
},
"samplePacks": [
{ "name": "bassWarsaw", "usage": "primary bass synth across 8+ tracks" },
{ "name": "FMRhodes1", "usage": "FM piano in insouciance, Mauerpark, and more" },
{ "name": "airports", "usage": "granular airport ambience in Quand on Décolle" },
{ "name": "aoc_fr", "usage": "French vocal collages in Fabuleux" },
{ "name": "orage", "usage": "storm/thunder sample bank — bass, hats, piano, voices in Nuit d'orage" },
{ "name": "weird_dialogs", "usage": "sci-fi movie dialogs about electricity in Bain électrique" },
{ "name": "come_bass / come_eguitar / come_voice", "usage": "3-part band sample pack in Venons Ensemble" },
{ "name": "suns_keys / suns_guitar / suns_voice", "usage": "Leifur James-inspired originals in L'Or Bleu" },
{ "name": "sunny_bass / sunny_brass", "usage": "custom sunny sample pack in Sunny Side Up" },
{ "name": "ghost", "usage": "haunted texture from 38C3 in Ghosts reprise" },
{ "name": "rhadamanthe_melo / rhadamanthe_vocal", "usage": "melodic piano + vocal in Sept 1 and Lady Perplexity" },
{ "name": "giorgio_syn", "usage": "Moroder tribute synth in Bain électrique and Lady Perplexity" },
{ "name": "acidOto3092", "usage": "arpeggiated acid synth in Sunny Side Up" },
{ "name": "moogBass", "usage": "Berlin warmth synth with Leslie/chorus in Mauerpark" },
{ "name": "bogdan_grime", "usage": "'I'm from Cardiff!' vocals in Jeudi Drill" },
{ "name": "jungle_breaks / breaks165", "usage": "breakbeat backbone across multiple tracks" },
{ "name": "temptation", "usage": "melodic sliced samples in Paris" },
{ "name": "fguitar", "usage": "granular guitar in Bain électrique" },
{ "name": "armora", "usage": "hawk-like percussion in insouciance" },
{ "name": "cpluck", "usage": "cello pluck bass in Sept 1" },
{ "name": "marimba1", "usage": "stereo melody in Jeudi Drill" }
],
"tracks": [
{
"name": "Quand on Décolle",
"file": "live/midi/nova/ambient/quand_on_decolle.tidal",
"bpm": 120,
"style": "ambient",
"ingredients": [
{ "type": "structure", "code": "setcps (120/60/4)", "description": "120 BPM quarter-note pulse" },
{ "type": "sample", "code": "\"airports\"", "description": "Airport ambience sample for texture" },
{ "type": "technique", "code": "slow 16 $ chop 16 $ \"airports\"", "description": "Slow granular chopping: 16 slices over 16 cycles" },
{ "type": "effect", "code": "# delaybus 51 (range 0 0.8 \"^54\") # delaytime \"^34\"", "description": "MIDI-controlled delay send with variable delay time" },
{ "type": "pattern", "code": "note (\"d3\" + scale \"mixolydian\" (\"[0 4 ... ]\" + ...))", "description": "Mixolydian bassWarsaw line with evolving variations" }
]
},
{
"name": "Fabuleux",
"file": "live/midi/nova/lounge/fabuleux.tidal",
"bpm": 93,
"style": "lounge",
"ingredients": [
{ "type": "structure", "code": "setcps (93/60/4)", "description": "93 BPM, downtempo lounge" },
{ "type": "sample", "code": "\"aoc_fr\"", "description": "French vocal collages cycled through someCyclesBy" },
{ "type": "technique", "code": "someCyclesBy \"<0!32 1!32>\" (>| n ...)", "description": "Probabilistic sample switching across 32-cycle blocks" },
{ "type": "pattern", "code": "note (\"[0,7(<3 5 [5 3] ...>,8,1)]\" + \"<d b4 g4 fs4>\" - 24)", "description": "Euclidean bassWarsaw, Bm-A-G-D progression" },
{ "type": "effect", "code": "# octerbus 71 (range 0 0.9 \"^55\")", "description": "MIDI-controlled octave effect for voice pitch shifting" }
]
},
{
"name": "Paris",
"file": "live/midi/nova/breaks/madeleine_de_paris.tidal",
"bpm": 80,
"style": "breaks",
"ingredients": [
{ "type": "structure", "code": "setcps (80/60/4)", "description": "80 BPM, mid-tempo breaks" },
{ "type": "sample", "code": "\"temptation\"", "description": "Temptation melodic samples sliced for narrative" },
{ "type": "technique", "code": "superimpose ((|* gain ...) . (# n 5) . (|+ note 12))", "description": "Acid superimposition an octave up with MIDI crossfade" },
{ "type": "pattern", "code": "note (\"d4 a3*<1!12 2!4> ...\") # \"vec2_synth_acid\"", "description": "Acid synth D-A alternation with rare chromatic variations" },
{ "type": "effect", "code": "# delay 0.5 # delayt \"<0.05 0.1 0.2 0.3 0.4>\" # delayfb 0.8", "description": "Evolving delay time creating shifting rhythmic echoes" }
]
},
{
"name": "Ghosts in the T01l3ts",
"file": "live/collab/ccc/ghosts_in_the_toilets.tidal",
"bpm": 160,
"style": "dnb",
"ingredients": [
{ "type": "structure", "code": "setcps (160/60/4)", "description": "160 BPM fast breakbeat/jungle" },
{ "type": "sample", "code": "\"ghost\" # n (slow 4 $ \"<0 1 2 3>\")", "description": "Ghost samples: 4 haunted variations" },
{ "type": "pattern", "code": "note (\"<e3 fs3 <gs3 d4> <a3 df4>>\" - 12) # \"bassWarsaw\"", "description": "Dark chromatic bass with diminished intervals" },
{ "type": "technique", "code": "slice 16 \"[0 .. 7] . <[0 .. 7]!3 [0 1 . [2 3]]>\"", "description": "16-slice break manipulation with alternate switching" },
{ "type": "sample", "code": "\"rhadamanthe_vocal:45\"", "description": "Vocal 'self-destruct sequence' drops" }
]
},
{
"name": "Nuit d'orage",
"file": "live/midi/nova/techno/techno_orage.tidal",
"bpm": 104,
"style": "techno",
"ingredients": [
{ "type": "structure", "code": "setcps (104/60/4)", "description": "104 BPM, dark techno" },
{ "type": "sample", "code": "\"orage\"", "description": "Storm/thunder bank used across all channels" },
{ "type": "pattern", "code": "n \"<~ 6 7 8 ... 20 ...>\" # \"orage\"", "description": "30+ storm piano samples in evolving narrative arc" },
{ "type": "technique", "code": "slow 4 $ ply \"4 . 2 0\"", "description": "Breathing rhythm: 4 hits, 2, silence" },
{ "type": "effect", "code": "# octersubbus 51 # octerbus 52 # crushbus 53", "description": "Triple MIDI effects stack on piano" }
]
},
{
"name": "Bain électrique",
"file": "live/midi/nova/breaks/bain_electrique.tidal",
"bpm": 128,
"style": "breaks",
"ingredients": [
{ "type": "structure", "code": "setcps (128/60/4)", "description": "128 BPM classic electro breaks" },
{ "type": "sample", "code": "\"weird_dialogs\"", "description": "Sci-fi dialogs about electricity, atomic energy, faith" },
{ "type": "technique", "code": "slice 16 \"0*[4 <4 4 1 ...>]\" $ chop 16 $ \"fguitar:55\"", "description": "Guitar granularly sliced with complex rhythmic patterns" },
{ "type": "pattern", "code": "note \"<fs3 [fs3@7 b3] ...>\" # \"giorgio_syn:0\"", "description": "Long-form Giorgio synth bass with chromatic movement" },
{ "type": "effect", "code": "# octersubbus 5 # octerbus 6", "description": "Global octave bus controlled by single MIDI knob" }
]
},
{
"name": "La fin de l'insouciance",
"file": "live/midi/nova/beatober/oct_16_haunted_house_insouciance.tidal",
"bpm": 120,
"style": "techno",
"ingredients": [
{ "type": "structure", "code": "setcps (120/60/4)", "description": "120 BPM, header: 'Released as: La Fin de l'Insouciance'" },
{ "type": "pattern", "code": "note \"<c3 c3 c3 c3 gs2 gs2 b2 b2>@5 ef3 d3 ...\" # \"bassWarsaw\"", "description": "Dark C minor bass with Ab and B natural tensions" },
{ "type": "sample", "code": "\"armora:1\"", "description": "Hawk-like percussion with extreme octave sub" },
{ "type": "technique", "code": "note (\"<[<g4 ...>@3 ...@2 ...@3] ...>\") # \"FMRhodes1\"", "description": "FM Rhodes bird-call with complex nested timing" },
{ "type": "effect", "code": "# delaybus 51 # delayt \"<h>\" # delayfb 0.25", "description": "Half-note delay with MIDI-controlled wet on Rhodes" }
]
},
{
"name": "Venons Ensemble",
"file": "live/midi/nova/dnb/venons_ensemble.tidal",
"bpm": 85,
"style": "dnb",
"ingredients": [
{ "type": "structure", "code": "setcps (85/60/4)", "description": "85 BPM (170 double-time), half-time DnB" },
{ "type": "sample", "code": "\"come_bass\" / \"come_eguitar\" / \"come_voice\"", "description": "Complete 3-part band sample pack" },
{ "type": "technique", "code": "someCyclesBy (\"^35\" - \"^91\") (>| n ...)", "description": "MIDI-controlled probability for vocal buildup" },
{ "type": "pattern", "code": "n \"<[0 0]!6 [1 1] <2 [2 2]>>\" # \"come_bass\"", "description": "Slow bass shifting only at bars 7-8" },
{ "type": "effect", "code": "# crushbus 71 # hpfbus 72", "description": "Dual voice processing: crush + HPF on same knob" }
]
},
{
"name": "L'Or Bleu",
"file": "live/midi/nova/lounge/suns_of_gold.tidal",
"bpm": 94,
"style": "lounge",
"ingredients": [
{ "type": "structure", "code": "setcps (94/60/4)", "description": "94 BPM downtempo — original of 'Blue Gold' collab" },
{ "type": "sample", "code": "\"suns_keys\" / \"suns_guitar\" / \"suns_voice\"", "description": "3-part Leifur James-inspired sample pack" },
{ "type": "technique", "code": "loopAt 8 $ chop 16 $ \"suns_keys\"", "description": "8-cycle piano loop chopped into 16 grains" },
{ "type": "pattern", "code": "note (\"[b,fs6]@1.5 [a,e6]@1.5 ...\" - 24) # \"bassWarsaw\"", "description": "Weighted timing bass (1.5+1.5+3) with Euclidean sub-patterns" },
{ "type": "effect", "code": "# lesliebus 42 # lrate 0.92 # lsize 1.9", "description": "MIDI Leslie speaker on piano for rotary warmth" }
]
},
{
"name": "Sunny Side Up",
"file": "live/midi/nova/lounge/sunny_side_up.tidal",
"bpm": 120,
"style": "lounge",
"ingredients": [
{ "type": "structure", "code": "setcps (120/60/4)", "description": "120 BPM upbeat lounge" },
{ "type": "sample", "code": "\"sunny_bass\" / \"sunny_brass\"", "description": "Custom sunny pack with bass and brass" },
{ "type": "technique", "code": "arp \"up\" $ note (\"0(3,8) ...\" + \"f gs c g\" + 12) # \"acidOto3092\"", "description": "Arpeggiated acid on F-Ab-C-G with Euclidean rhythm" },
{ "type": "pattern", "code": "n \"<0 0 0 0 1 1 1 <0 1 2 ...>>\" # \"sunny_bass\"", "description": "Bass staying on root for 4 bars then slowly varying" },
{ "type": "effect", "code": "# octersubbus 42 # octersubsubbus 43", "description": "Dual sub-octave on same knob for massive low end" }
]
},
{
"name": "Premier Septembre",
"file": "live/collab/baba/sept1.tidal",
"bpm": 120,
"style": "collab",
"ingredients": [
{ "type": "structure", "code": "setcps (120/60/4)", "description": "120 BPM, opens with 'À Mon Amour'" },
{ "type": "sample", "code": "\"rhadamanthe_melo:1\"", "description": "Rhadamanthe piano split into begin/mid/end narrative" },
{ "type": "technique", "code": "slow 2 \"<begin ~ begin ...>/2\" # cut 51 # begin 0 # end 0.2", "description": "Piano surgically split into 3 cue points" },
{ "type": "pattern", "code": "note (\"<<[b3@5 a3 b3 a3] ...>> [e3@7 b3]>\") # \"cpluck\"", "description": "Cello pluck B-A oscillation resolving to E" },
{ "type": "effect", "code": "# cps ((range 60 180 \"^29\")/60/4)", "description": "MIDI live tempo from 60 to 180 BPM — real-time manipulation" }
]
},
{
"name": "Lady Perplexity",
"file": "live/midi/nova/breaks/lady_perplexity.tidal",
"bpm": 138,
"style": "breaks",
"ingredients": [
{ "type": "structure", "code": "setcps (138/60/4)", "description": "138 BPM fast breaks" },
{ "type": "sample", "code": "\"rhadamanthe_vocal:1\"", "description": "The Lady — vocal chopped into 16 grains over 8 cycles" },
{ "type": "technique", "code": "sometimesBy \"^55\" (rev) . sometimesBy \"^35\" (juxBy 0.25 ...)", "description": "Triple probability: reversal, stereo micro-loops, euclidean masking" },
{ "type": "pattern", "code": "note (\"c3 <d3 ... [d3@3 f3]> e3 <g3 b3 a3 c4>\") # \"bassWarsaw\"", "description": "C minor walking bass with chromatic upper voice" },
{ "type": "effect", "code": "# crushbus 51 (range 16 3.5 \"^54\")", "description": "Bitcrush on Giorgio pad for lo-fi harmonic layer" }
]
},
{
"name": "L'été à Mauerpark",
"file": "live/midi/nova/techno/ete_a_mauerpark.tidal",
"bpm": 120,
"style": "techno",
"ingredients": [
{ "type": "structure", "code": "setcps (120/60/4)", "description": "120 BPM, Berlin-inspired techno" },
{ "type": "pattern", "code": "note (\"<[g4 c ef@14] ...>\" - 24) # \"[bass2:1,bassWarsaw]\"", "description": "Dual bass on Cm with long sustained notes and brief melodic hits" },
{ "type": "technique", "code": "arp \"<up!6 down downup>\" $ note (... + \"c'maj'4\" - 12) # \"FMRhodes1\"", "description": "Arpeggiated Rhodes chords cycling up/down/downup" },
{ "type": "sample", "code": "\"moogBass\"", "description": "Moog with MIDI attack/release, Leslie and chorus" },
{ "type": "effect", "code": "# lesliebus 102 # chorus (range 0 2 \"^19\")", "description": "Leslie + chorus for Berlin warmth on separate MIDI knobs" }
]
},
{
"name": "Jeudi Drill",
"file": "live/collab/raph/jeudrill.tidal",
"bpm": 140,
"style": "drill",
"ingredients": [
{ "type": "structure", "code": "setcps (140/60/4)", "description": "140 BPM fast drill/grime" },
{ "type": "sample", "code": "\"bogdan_grime\"", "description": "'I'm from Cardiff!', 'Pints' — grime vocals" },
{ "type": "technique", "code": "juxBy 0.4 rev $ note ... # \"marimba1\"", "description": "Marimba with stereo juxtaposed reversal at 40%" },
{ "type": "pattern", "code": "slow 2 $ note (\"<<fs ...>> b4>\" - 12 + 2) # \"giorgio_syn:1\"", "description": "Giorgio bass with long weighted F# notes resolving" },
{ "type": "effect", "code": "# crushbus 101 # octersub 1", "description": "Bitcrush + sub-octave on 90s synth pad" }
]
}
]
}
......@@ -8,7 +8,7 @@ description: "Afterparty set for Raise Summit."
ctaURL: ""
ctaText: ""
video: ""
audio: ""
audio: "https://parvagues.bandcamp.com/track/ton-num-ro"
archive: ""
tags: ["livecoding", "raise", "afterparty", "techno"]
---
......
......@@ -60,6 +60,21 @@ export async function getLiveData(slug) {
throw new Error(`Live with slug "${slug}" not found`);
}
export function getLiveTracks(slug) {
const years = fs.readdirSync(livesDirectory).filter(item =>
fs.statSync(path.join(livesDirectory, item)).isDirectory()
);
for (const year of years) {
const tracksPath = path.join(livesDirectory, year, slug, 'tracks.json');
if (fs.existsSync(tracksPath)) {
return JSON.parse(fs.readFileSync(tracksPath, 'utf8'));
}
}
return null;
}
export function getLivesImages(slug) {
const years = fs.readdirSync(livesDirectory).filter(item =>
fs.statSync(path.join(livesDirectory, item)).isDirectory()
......
import { getAllLives } from '@/lib/livesData';
import Layout from '@/components/parvagues/Layout';
import Hero from '@/components/parvagues/Hero';
import BioSection from '@/components/parvagues/BioSection';
import AsSeenAt from '@/components/parvagues/AsSeenAt';
import TourTimeline from '@/components/parvagues/TourTimeline';
import MusicSection from '@/components/parvagues/MusicSection';
import VideoSection from '@/components/parvagues/VideoSection';
......@@ -10,6 +12,8 @@ export default function ParVagues({ lives }) {
return (
<Layout title="ParVagues - Musique Algorithmique">
<Hero />
<BioSection />
<AsSeenAt />
<TourTimeline lives={lives} />
<MusicSection />
<VideoSection />
......
import { useState, useEffect } from 'react';
import Head from 'next/head';
import Image from 'next/image';
import { getAllLives, getLiveData, getLivesImages, getLiveTracks } from '@/lib/livesData';
import Layout from '@/components/parvagues/Layout';
import Countdown from '@/components/parvagues/Countdown';
import ImageGallery from '@/components/parvagues/ImageGallery';
import { format } from 'date-fns';
import { fr } from 'date-fns/locale';
import ReactMarkdown from 'react-markdown';
import Prism from 'prismjs';
import 'prismjs/components/prism-haskell';
import 'prismjs/themes/prism-tomorrow.css';
function TrackIngredient({ ingredient }) {
return (
<div className="bg-black/30 rounded-lg p-3 border border-white/5">
<div className="flex items-center gap-2 mb-1.5">
<span className="text-[10px] tracking-widest uppercase px-2 py-0.5 rounded-full border border-[var(--neon-high)]/20 text-[var(--neon-high)]">
{ingredient.type}
</span>
</div>
<code className="block text-xs text-[var(--text-muted)] font-mono leading-relaxed mb-1 overflow-x-auto">
{ingredient.code}
</code>
<p className="text-[11px] text-[var(--text-muted)]">{ingredient.description}</p>
</div>
);
}
function TracksSection({ tracks }) {
if (!tracks) return null;
return (
<div className="mb-16">
<div className="flex items-center gap-4 mb-6">
<h2 className="font-display text-2xl font-bold text-white">
<span className="text-[var(--neon-high)]">#</span> Tracks
</h2>
<div className="h-px bg-[var(--neon-high)]/20 flex-grow" />
</div>
{tracks.styleDistribution && (
<div className="flex flex-wrap gap-2 mb-8">
{Object.entries(tracks.styleDistribution).map(([style, count]) => (
<span key={style} className="px-3 py-1 text-[11px] tracking-widest uppercase rounded-full border border-white/10 text-[var(--text-muted)]">
{style} ×{count}
</span>
))}
{tracks.bpmRange && (
<span className="px-3 py-1 text-[11px] tracking-widest uppercase rounded-full border border-[var(--neon-high)]/20 text-[var(--neon-high)]">
{tracks.bpmRange[0]}{tracks.bpmRange[1]} BPM
</span>
)}
</div>
)}
<div className="space-y-6">
{tracks.tracks.map((track, i) => (
<details key={track.name} className="group bg-white/[0.02] border border-white/[0.06] rounded-xl overflow-hidden">
<summary className="flex items-center gap-4 px-5 py-4 cursor-pointer hover:bg-white/[0.03] transition-colors">
<span className="font-mono text-xs text-[var(--text-muted)] w-6">{String(i + 1).padStart(2, '0')}</span>
<span className="font-display font-semibold text-sm flex-grow">{track.name}</span>
{track.bpm && <span className="text-[11px] text-[var(--text-muted)] font-mono">{track.bpm} BPM</span>}
{track.style && <span className="text-[10px] tracking-wider uppercase text-[var(--text-muted)]">{track.style}</span>}
</summary>
<div className="px-5 pb-5 pt-2 space-y-3 border-t border-white/[0.04]">
{track.ingredients.map((ing, j) => (
<TrackIngredient key={j} ingredient={ing} />
))}
{track.file && (
<p className="text-[10px] text-[var(--text-muted)] font-mono mt-2">
src: {track.file}
</p>
)}
</div>
</details>
))}
</div>
</div>
);
}
export default function LiveEvent({ live, images, tracks }) {
const [phase, setPhase] = useState('early'); // early, J-14, J-7, J-3, live, post
const [isPostEvent, setIsPostEvent] = useState(false);
useEffect(() => {
// Highlight code blocks
Prism.highlightAll();
}, [live, phase]);
useEffect(() => {
const checkDate = () => {
const now = new Date();
const eventDate = new Date(live.date);
if (now > eventDate) {
setIsPostEvent(true);
setPhase('post');
}
};
checkDate();
const interval = setInterval(checkDate, 60000); // Check every minute
return () => clearInterval(interval);
}, [live.date]);
// Determine which content to show based on phase
const getTeasingContent = () => {
if (isPostEvent) return null;
// Logic to select teasing content from frontmatter based on phase
// Assuming frontmatter has fields like teasing_j7, teasing_j3 etc.
// For now, we'll just show the main description + countdown
return (
<div className="text-center mb-12">
<div className="inline-block p-6 bg-black/50 backdrop-blur-md rounded-xl border border-purple-500/30 shadow-[0_0_30px_rgba(137,0,179,0.2)]">
<h3 className="text-purple-400 mb-4 font-mono text-sm">&gt; INITIALIZING_EVENT</h3>
<Countdown targetDate={live.date} onPhaseChange={setPhase} />
</div>
</div>
);
};
return (
<Layout title={`${live.title} - ParVagues`}>
<div className="max-w-4xl mx-auto">
{/* Header Info */}
<div className="text-center mb-16">
<div className="inline-block px-3 py-1 mb-4 text-xs font-mono text-purple-300 border border-purple-500/30 rounded-full">
{(() => {
const date = new Date(live.date);
return isNaN(date.getTime()) ? 'Date TBD' : format(date, 'dd MMMM yyyy', { locale: fr });
})()}
</div>
<h1 className="text-4xl md:text-6xl font-bold mb-4 bg-gradient-to-r from-white to-gray-400 bg-clip-text text-transparent">
{live.title}
</h1>
<p className="text-xl text-gray-400 flex items-center justify-center gap-2">
<span className="w-2 h-2 bg-purple-500 rounded-full animate-pulse"></span>
{live.location}
</p>
</div>
{/* Poster */}
{live.frontmatter.poster && (
<div className="mb-16 rounded-2xl overflow-hidden border border-white/[0.06]">
<Image
src={live.frontmatter.poster}
alt={`${live.title} poster`}
width={1200}
height={1710}
className="w-full h-auto"
priority
/>
</div>
)}
{/* Countdown / Teasing */}
{!isPostEvent && getTeasingContent()}
{/* Main Content */}
<div className="prose prose-invert prose-purple max-w-none mb-16 bg-black/30 p-8 rounded-2xl border border-white/5">
<ReactMarkdown>{live.content}</ReactMarkdown>
</div>
{/* Tracks & Code */}
<TracksSection tracks={tracks} />
{/* Gallery (Post-event or if images exist) */}
{images && images.length > 0 && (
<div className="mb-16">
<div className="flex items-center gap-4 mb-8">
<h2 className="text-2xl font-bold text-white">
<span className="text-purple-500">#</span> Galerie
</h2>
<div className="h-px bg-purple-500/30 flex-grow"></div>
</div>
<ImageGallery images={images} />
</div>
)}
</div>
</Layout>
);
}
export async function getStaticPaths() {
const lives = getAllLives();
const paths = lives.map((live) => ({
params: { slug: live.slug },
}));
return { paths, fallback: false };
}
export async function getStaticProps({ params }) {
const live = await getLiveData(params.slug);
const images = getLivesImages(params.slug);
const tracks = getLiveTracks(params.slug);
return {
props: {
live,
images,
tracks,
},
revalidate: 60,
};
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment