Skip to content

05 — Interaction Design

“Nintendo maakt al 40 jaar de meest satisfying games ter wereld. Het geheim is wat game designers ‘juice’ noemen — de overdreven feedback op elke actie die het brein vertelt: Dat ging goed. Doe het nog eens.”

Dit document beschrijft de volledige interactielaag van Beam: geluiden, micro-animaties, cursors, ambient muziek, en het Nintendo Juice principe. Deze laag maakt de editor niet alleen functioneel maar voelbaar goed.


  1. Nintendo Juice — Het Principe
  2. Micro-geluiden
  3. Micro-animaties
  4. Custom Cursors
  5. Ambient Muziek
  6. Interaction Design in de Styling Builder
  7. Technische Implementatie
  8. Toegankelijkheid
  9. Bouwvolgorde

In Mario pakt je een muntje → geluidje + sparkle + counter animatie + lichte screen shake. Vier feedback-signalen voor één simpele actie. Het voelt geweldig terwijl je rationeel weet dat het “maar een muntje” is.

In Zelda open je een kist → slow-motion + icoontje stijgt op + fanfare + tekst beschrijving. Een moment van ceremonie voor een ontdekking.

De vuistregel voor Beam: elke gebruikersactie verdient minimaal twee feedback-signalen (visueel + audio, of visueel + beweging). Eén signaal voelt klinisch. Twee voelen menselijk. Drie voelen magisch.

Nintendo Referenties → Beam Toepassingen

Section titled “Nintendo Referenties → Beam Toepassingen”
Nintendo ReferentieBeam Toepassing
Mario munt (geluid + sparkle + counter)Block toevoegen: pop + fade-in + block counter update
Zelda kist (fanfare + reveal)Addon activatie: booster pack unboxing (kaart voor kaart)
Mario star (muziek verandert + onkwetsbaarheid)Publiceren: confetti + chime + dashboard viert mee
Zelda Korok (verrassing op onverwachte plek)Secret synergie block ontdekt: glow + speciaal geluidje
Animal Crossing museum (curated collectie)Beam Codex: je block collectie groeit en wordt mooier
Mario Kart boost (screen blur + snelheidslijnen)Undo/redo: shimmer effect over het canvas
Zelda cooking (ingrediënten combineren → resultaat)Pattern maken: blocks combineren → iets nieuws
Wii Sports bowling (iedereen kan het)Guardrails: je kunt het niet lelijk maken

Het ultieme doel: de editor voelt als een instrument dat je bespeelt. Niet als een formulier dat je invult. Na een uur bouwen voel je je niet uitgeput maar energiek — omdat de tool met je meewerkt.


Subtiele, warme geluiden die feedback geven zonder opdringerig te zijn. Denk aan Notion of Linear — niet aan arcade games.

ActieGeluidKarakter
Block toevoegenZacht “pop”Iets nieuws verschijnt
Block verwijderenZacht “whoosh”Iets verdwijnt elegant
Block verplaatsen (drag)Subtiel “slide”Beweging, gewicht
Block droppenZacht “thud”Iets landt op zijn plek
Pagina opslaanWarm “ding”Bevestiging, veiligheid
Pagina publicerenGroter “chime”Feestelijk moment
Undo/RedoZacht “tick/tock”Tijd terugdraaien
SelectieClickPrecies, responsief
Block groeperenHarmonisch “merge” geluidDingen komen samen
FoutmeldingZacht “bonk”Nope, maar niet boos
Achievement unlockSubtiel “level up” chimeHerkenbaar, belonend
Site liveConfetti + triumfantelijke chimeHet grote moment
GildeSound CharacterInspiratie
⚕️ HealersKlankschaal, natuur, waterSpa-achtig, meditatief
🎨 CreatorsCreatieve tonen, potlood, canvasAtelier-sfeer
🏪 MerchantsKassageluiden, verpakkendWinkelsfeer, warm
📚 ScholarsBoekpagina’s, pen op papierBibliotheek, focus
🍽️ HostsBestek, kurk, deurbelletjeGastvrij, huiselijk
🔧 BuildersHout, metaal, precisiegereedschapWerkplaats, ambacht
  • Alle geluiden standaard aan maar met volume slider (0–100%)
  • Geluiden als kleine .webm bestanden (< 5KB elk), opgeslagen op CF R2
  • Web Audio API voor low-latency playback
  • Geladen op eerste interactie — niet bij page load
  • Geluidsprofiel gekoppeld aan het gekozen gilde (verandert automatisch mee)

Elke interactie heeft een subtiele beweging die het resultaat bevestigt:

ActieAnimatie
Block hoverZachte glow/highlight, licht optillen (2px shadow)
Block selecterenSmooth border-animatie, sidebar slide-in
Block toevoegenFade-in + slight scale (0.95 → 1.0)
Block verwijderenShrink + fade-out
Drag startBlock tilt licht, shadow groeit (lifted)
DropBounce-settle op doelpositie
Pagina opslaanCheckmark animatie in save-button
Pagina publicerenButton → confetti burst (eenmalig per publicatie)
AchievementToast slide-in met glow effect
UndoSubtiele “rewind” shimmer over het canvas
Block groeperenElements bewegen naar elkaar toe → merge animatie

Alle animaties gebruiken spring physics — niet lineaire beziers. Dit geeft een natuurlijk gevoel alsof objecten massa hebben.

Implementatie: gebruik motion (het standalone framework-agnostische pakket) of CSS spring bezier waarden:

import { motion } from 'motion'
// Block toevoegen
<motion.div
initial={{ opacity: 0, scale: 0.95 }}
animate={{ opacity: 1, scale: 1 }}
transition={{ type: 'spring', stiffness: 400, damping: 25 }}
/>
// Drag feedback
<motion.div
whileDrag={{ scale: 1.02, rotate: 1, boxShadow: '0 8px 24px rgba(0,0,0,0.15)' }}
transition={{ type: 'spring', stiffness: 600, damping: 30 }}
/>
// Drop landing
<motion.div
animate={{ y: [4, 0] }}
transition={{ type: 'spring', stiffness: 500, damping: 20 }}
/>

Eenmalig per publicatie — het grote moment verdient ceremonie:

confetti.ts
import confetti from 'canvas-confetti'
export function celebratePublish() {
confetti({
particleCount: 100,
spread: 70,
origin: { y: 0.6 },
colors: ['#1B4D7A', '#D4883E', '#F5F0EB'] // brand colors
})
}

De standaard cursor is functioneel maar emotieloos. Een custom cursor voegt persoonlijkheid toe.

ContextCursor
DefaultBeam branded pointer (subtiel, niet gimmicky)
Over blocksCrosshair met zachte glow
Drag modusGrab hand → grabbing hand (met shadow)
Text editingBeam branded I-beam
Color pickerPipet/eyedropper
ResizeElegante resize arrows
  • ⚕️ Healers: zachte, ronde cursor
  • 🎨 Creators: potlood/pen cursor
  • 🔧 Builders: precision crosshair
  • SVG cursors via CSS cursor: url() voor performance
  • Fallback naar system cursors bij prefers-reduced-motion
  • Gebruiker kan kiezen: Beam cursor / Gilde cursor / Systeem cursor
.editor-canvas {
cursor: url('/cursors/beam-default.svg') 4 4, auto;
}
.editor-canvas[data-dragging] {
cursor: url('/cursors/beam-grabbing.svg') 12 12, grabbing;
}

Optioneel, subtiel, en contextbewust. Niet als achtergrondmuziek die je verdraagt, maar als sfeer die je dag beter maakt.

ModusMuziekKarakter
Editor (bouwen)Lo-fi ambient, soft focusConcentratie, flow
Dashboard (plannen)Licht, positief, ruimtelijkOverzicht, controle
Preview (bewonderen)Stiller, meer ruimteHet resultaat staat centraal
PublicerenKort moment van triomfClimax → rust
GildeMuziekstijl
⚕️ HealersAmbient pads, klankschalen, natuur
🎨 CreatorsLo-fi beats, creatieve piano, vinyl crackle
🏪 MerchantsJazzy, upbeat lo-fi, warm
📚 ScholarsClassical-inspired ambient, focus
🍽️ HostsCafé jazz, acoustic, gezellig
🔧 BuildersMinimal techno-ambient, industrieel maar warm
  • Standaard uit — opt-in bij eerste gebruik: “Wil je ambient muziek terwijl je bouwt?”
  • Volume slider onafhankelijk van geluidseffecten
  • Naadloze loops (geen hoorbare start/stop)
  • Context-switch crossfade (editor → dashboard → preview)
  • Respecteer browser autoplay policies
  • ~5 tracks per profiel, random rotatie
  • Muziek als streaming vanuit CF R2 (geen grote downloads)

6. Interaction Design in de Styling Builder

Section titled “6. Interaction Design in de Styling Builder”

De styling builder is niet alleen functioneel — hij voelt goed. Elke actie heeft minimaal twee feedback-signalen:

ActieVisueelAudio
Kleur wijzigenRipple-animatie door alle tokens die de kleur gebruikenZacht “toon” geluid (pitch volgt kleur-hue)
Shade schaal hergenererenVloeiende overgang van oude naar nieuwe tintenOplopende toonreeks (laag→hoog)
Font wisselenSmooth morph van oude naar nieuwe font in canvasZacht “page turn”
Radius aanpassenAlle elementen animeren met spring physicsSubtiel “click” per stap
Guardrail ✅Groen vinkje fade-inZacht “ding”
Guardrail ⚠️Element schudt licht + suggestie slide-inZacht “bonk”
Token opslaanCheckmark animatie op save-buttonWarm “ding”
PublicerenConfetti burst + tokens “settelen” met bounceTriumfantelijk “chime”
Style Pack ladenKaart-voor-kaart onthulling van de nieuwe stijlUnboxing geluidjes

Geluidsprofiel: volgt het gilde van de gebruiker. Een Healer hoort klankschaal-achtige tonen, een Creator hoort atelier-geluiden.

Wanneer de gebruiker de radius aanpast, animeren alle elementen in het preview-canvas simultaan:

// Alle border-radius-waarden animeren synchroon
const radiusPresets = {
none: '0px',
small: '4px',
medium: '8px',
large: '16px',
pill: '9999px'
}
// Spring physics voor de overgang
animate('.preview-element', {
borderRadius: radiusPresets[newPreset]
}, { type: 'spring', stiffness: 300, damping: 20 })

interface BeamAudioEngine {
// Sound effects
playSound(id: SoundId, options?: { volume?: number }): void
setSoundVolume(volume: number): void // 0-100
setSoundProfile(gilde: GildeId): void
setSoundEnabled(enabled: boolean): void
// Ambient music
playAmbient(context: 'editor' | 'dashboard' | 'preview'): void
stopAmbient(): void
setMusicVolume(volume: number): void
setMusicProfile(gilde: GildeId): void
setMusicEnabled(enabled: boolean): void
crossfade(toContext: string, duration: number): void
}
type SoundId =
| 'block-add' | 'block-remove' | 'block-drop' | 'block-drag'
| 'save' | 'publish' | 'undo' | 'redo'
| 'select' | 'group' | 'error'
| 'achievement' | 'site-live'
// In Zustand store actions — sound wordt automatisch afgespeeld
addBlock: (type, index) => {
set((state) => { /* block logic */ })
audioEngine.playSound('block-add')
},
removeBlock: (blockId) => {
set((state) => { /* block logic */ })
audioEngine.playSound('block-remove')
},
savePage: async () => {
await api.savePage(/* ... */)
audioEngine.playSound('save')
},
publishPage: async () => {
await api.publishPage(/* ... */)
audioEngine.playSound('publish')
celebratePublish() // confetti
},
import { animate, spring } from 'motion'
// Block toevoegen
export function animateBlockAdd(element: HTMLElement) {
animate(element,
{ opacity: [0, 1], scale: [0.95, 1] },
{ duration: 0.2, easing: spring({ stiffness: 400, damping: 25 }) }
)
}
// Block verwijderen
export function animateBlockRemove(element: HTMLElement): Promise<void> {
return animate(element,
{ opacity: [1, 0], scale: [1, 0.95] },
{ duration: 0.15 }
).finished
}
// Undo shimmer
export function animateUndo(canvas: HTMLElement) {
animate(canvas,
{ filter: ['brightness(1)', 'brightness(1.05)', 'brightness(1)'] },
{ duration: 0.3, easing: 'ease-out' }
)
}
r2/beam-assets/
sounds/
default/
block-add.webm
block-remove.webm
save.webm
publish.webm
achievement.webm
...
healers/
block-add.webm ← klankschaal variant
...
creators/
block-add.webm ← atelier variant
...
music/
healers/
editor-01.mp3
editor-02.mp3
...
creators/
...

Alle animaties respecteren de gebruikersvoorkeur:

@media (prefers-reduced-motion: reduce) {
.beam-animated {
transition: none !important;
animation: none !important;
}
}
// In motion wrappers
const prefersReducedMotion = window.matchMedia(
'(prefers-reduced-motion: reduce)'
).matches
const transition = prefersReducedMotion
? { duration: 0 }
: { type: 'spring', stiffness: 400, damping: 25 }

Bij prefers-reduced-motion: reduce worden geluiden standaard uitgeschakeld. De gebruiker kan ze expliciet aanzetten via de instellingen.

  • Volume slider (0–100%) voor geluidseffecten, apart van muziek
  • Muziek is opt-in — nooit automatisch
  • Cursor kan worden teruggezet naar system cursor
  • Alle interactie-feedback is optioneel (maar aanbevolen)

  1. Web Audio API wrapper (BeamAudioEngine)
  2. 10 basis geluiden: add, remove, drag, drop, save, publish, undo, redo, select, error
  3. Sound settings in user preferences
  4. prefers-reduced-motion support
  5. Assets op CF R2
  1. Spring physics setup (motion library)
  2. Block operaties: fade-in/out, drag tilt, drop bounce
  3. Sidebar slide-in bij block selectie
  4. Checkmark animatie op save
  5. Confetti bij publiceren
  1. Geluidsprofiel per gilde (6 profielen, elk met 10+ varianten)
  2. Muziekprofiel per gilde (5 tracks per profiel)
  3. Context-switch crossfade (editor/dashboard/preview)
  1. Beam cursor SVGs
  2. Context-afhankelijke cursors (grab, I-beam, eyedropper)
  3. Gilde cursor themes
  1. Ripple-animatie bij kleurpropagatie
  2. Pitch-variatie bij kleurwijziging (hue → toon)
  3. Toonreeks bij shade generatie
  4. Unboxing geluidjes bij Style Pack wisselen
  1. Muziek streaming systeem
  2. Naadloze loops
  3. Context-switch crossfade
  4. Opt-in flow bij eerste gebruik