Creative Technologies Lab | dokuWiki

Repository of academic adventures, experimental technology, accidental brilliance, and collaborative nerdery.

User Tools

Site Tools


extras:codikon:p5js:wireframe-ideen-generator

This is an old revision of the document!


<!doctype html> <html lang=“de”> <head> <meta charset=“utf-8”> <meta name=“viewport” content=“width=device-width, initial-scale=1”> <title>Wireframe-Ideen-Generator</title> <style>

:root {
  --k: #111111; --o: #FF6A13; --b: #2B5BFF;
  --g1: #F3F3F3; --g2: #D4D4D4; --g3: #5D5D5D;
  color-scheme: light;
}
* { box-sizing: border-box; }
body { margin: 0; background: #fff; color: var(--k); font: 16px/1.4 Arial, Helvetica, sans-serif; }
#app { max-width: 1000px; margin: 0 auto; padding: 16px; }
h1 { font-size: 22px; margin: 0 0 4px; }
.hint { margin: 0 0 14px; color: var(--g3); font-size: 14px; }
.bar { display: flex; flex-wrap: wrap; gap: 10px 18px; align-items: center; margin-bottom: 14px; }
.grp { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.grp > span { font-size: 13px; font-weight: bold; color: var(--g3); text-transform: uppercase; }
.seg { display: inline-flex; }
button {
  font: inherit; font-weight: bold; font-size: 15px; color: var(--k); background: #fff;
  border: 2px solid var(--k); border-radius: 8px; padding: 8px 14px; cursor: pointer;
}
button:hover { background: var(--g1); }
button:focus-visible { outline: 3px solid var(--b); outline-offset: 2px; }
.seg button { border-radius: 0; margin-left: -2px; padding: 8px 12px; }
.seg button:first-child { border-radius: 8px 0 0 8px; margin-left: 0; }
.seg button:last-child { border-radius: 0 8px 8px 0; }
.seg button[aria-pressed="true"] { background: var(--k); color: #fff; }
button.primary { background: var(--o); border-color: var(--o); font-size: 17px; padding: 10px 20px; }
button.primary:hover { background: #ff7f33; border-color: #ff7f33; }
#stage { width: 100%; }
#stage canvas { display: block; }
.panel { border: 2px solid var(--k); border-radius: 14px; padding: 14px 18px; margin-top: 14px; }
.panel h2 { margin: 0 0 8px; font-size: 13px; color: var(--g3); text-transform: uppercase; }
#brief { margin: 0; white-space: pre-wrap; font: 16px/1.6 Arial, Helvetica, sans-serif; }
.panel { position: relative; }
.iconbtn { position: absolute; top: 6px; right: 11px; width: 36px; height: 36px; padding: 0; border: 0; background: transparent;
           color: var(--k); display: flex; align-items: center; justify-content: center; border-radius: 8px; }
.iconbtn:hover { background: var(--g1); }
.timer { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
#time { font-size: 40px; font-weight: bold; min-width: 120px; font-variant-numeric: tabular-nums; }
#time.done { color: var(--o); }
.note { color: var(--b); font-size: 14px; font-weight: bold; }
@media (max-width: 480px) { #app { padding: 12px; } #time { font-size: 32px; min-width: 96px; } }

</style> </head> <body>

<h1>Wireframe-Ideen-Generator</h1>
<p class="hint">Klick auf eine Karte würfelt sie neu. Das Schloss hält eine Karte fest.</p>
<div class="bar">
  <button class="primary" id="rollAll" type="button">Alles würfeln</button>
  <div class="grp" role="group" aria-label="Anzahl der Karten">
    <span>Karten</span>
    <div class="seg">
      <button type="button" data-n="3" aria-pressed="false">3 · Basis</button>
      <button type="button" data-n="6" aria-pressed="true">6 · Voll</button>
    </div>
  </div>
  <div class="grp" role="group" aria-label="Chaos-Level">
    <span>Chaos</span>
    <div class="seg">
      <button type="button" data-l="1" aria-pressed="false">realistisch</button>
      <button type="button" data-l="2" aria-pressed="false">schräg</button>
      <button type="button" data-l="3" aria-pressed="true">absurd</button>
    </div>
  </div>
</div>
<div id="stage" role="img" aria-label="Zufällig gewürfelte Karten: Produkt, Kundin, Thema, Gerät, Hauptaktion, Twist. Die Auswahl steht auch als Text unter den Karten."></div>
<div class="panel" id="briefPanel">
  <h2>Dein Briefing</h2>
  <button type="button" class="iconbtn" id="copyTxt" aria-label="Briefing kopieren" title="Briefing kopieren">
    <svg id="icCopy" width="28" height="28" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
      <rect x="8" y="8" width="12" height="12" rx="3"/><path d="M16 8V6a2 2 0 0 0-2-2H6a2 2 0 0 0-2 2v8a2 2 0 0 0 2 2h2"/>
    </svg>
    <svg id="icDone" width="28" height="28" viewBox="0 0 24 24" fill="none" stroke="#2B5BFF" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" style="display:none">
      <path d="M5 12.5l4.5 4.5L19 7.5"/>
    </svg>
  </button>
  <pre id="brief" aria-live="polite"></pre>
</div>
<div class="panel">
  <h2>Timer</h2>
  <div class="timer">
    <div id="time" aria-live="off">10:00</div>
    <button type="button" id="tStart">Start</button>
    <button type="button" id="tReset">Zurück</button>
    <div class="seg" role="group" aria-label="Zeit wählen">
      <button type="button" data-min="10" aria-pressed="true">10 Min · Skizzen</button>
      <button type="button" data-min="25" aria-pressed="false">25 Min · Mid-Fi</button>
    </div>
  </div>
</div>

<script src=“https://cdnjs.cloudflare.com/ajax/libs/p5.js/1.9.4/p5.min.js”

      onerror="document.getElementById('stage').textContent='p5.js konnte nicht geladen werden. Bitte Internetverbindung prüfen.'"></script>

<script> /* ———- Farben (wie in den Praktikumsfolien) ———- */ const K = '#111111', O = '#FF6A13', B = '#2B5BFF', G1 = '#F3F3F3', G2 = '#D4D4D4', G3 = '#5D5D5D';

/* ———- Inhalte: [Text, Chaos-Stufe 1–3, (Breite, Höhe nur bei Gerät)] ———- */ const POOLS = [

{ name: 'Produkt', label: 'PRODUKT', icon: 'produkt', items: [
  ['Mobile App', 1], ['Website mit Landingpage', 1], ['Onlineshop', 1], ['Buchungs-Plattform', 1],
  ['Dashboard', 1], ['Lern-Plattform', 1], ['Karten- und Navigations-App', 1], ['Mitglieder-Portal', 1],
  ['Bezahlvorgang (Checkout)', 1], ['Onboarding-Strecke', 1], ['Chat-Oberfläche', 1],
  ['Dating-App', 2], ['Tauschbörse', 2], ['Bewertungsportal', 2], ['Podcast-App', 2],
  ['Rezept-Plattform', 2], ['Smart-Home-Steuerung', 2], ['Crowdfunding-Seite', 2], ['Kiosk-Terminal', 2],
  ['Social Network für genau zwei Personen', 3], ['Kontrollzentrum mit zu vielen Knöpfen', 3],
  ['Beschwerde-Hotline als App', 3], ['Wahl-O-Mat für Alltagsentscheidungen', 3], ['Notfall-Knopf-Portal', 3]
]},
{ name: 'Kundin', label: 'KUNDIN', icon: 'kundin', items: [
  ['Studentin im Prüfungsstress', 1], ['Landwirtin mit schlechtem Netz', 1], ['Busfahrerin im Schichtdienst', 1],
  ['Hebamme auf Nachtschicht', 1], ['Hochzeitsplanerin mit 14 Terminen', 1], ['Bibliothekarin, die alles sortiert', 1],
  ['Ärztin mit 3 Minuten Zeit', 1], ['Pilotin und alleinerziehend', 1],
  ['Gärtnerin mit 300 Zimmerpflanzen', 2], ['Marathonläuferin ohne Orientierungssinn', 2],
  ['Kapitänin eines Fischkutters ohne Empfang', 2], ['Zirkusdirektorin mit Lampenfieber', 2],
  ['Schornsteinfegerin mit Höhenangst', 2], ['Influencerin, die privat offline lebt', 2],
  ['Detektivin, die ständig etwas verlegt', 2], ['Köchin, die nie Rezepte liest', 2],
  ['Zeitreisende aus dem Jahr 1850', 3], ['Drachenbesitzerin mit Kleinunternehmen', 3], ['Hexe im Homeoffice', 3],
  ['Außerirdische Austauschstudentin', 3], ['Piratenkapitänin im Ruhestand', 3], ['Fee mit Burnout', 3],
  ['Waschbär-Anwältin', 3], ['Königin eines sehr kleinen Landes', 3], ['Yeti-Vorsitzende des Bergvereins', 3],
  ['Roboter-Oma mit Sprachfehler', 3], ['Einhorn-Züchterin mit Pollenallergie', 3]
]},
{ name: 'Thema', label: 'THEMA', icon: 'thema', items: [
  ['Nachbarschaftshilfe', 1], ['Zimmerpflanzen-Pflege', 1], ['Mittagspause', 1], ['Geburtstagsüberraschung', 1],
  ['Mülltrennung', 1], ['Mitfahrgelegenheiten', 1], ['Kaffee', 1], ['Urlaubsplanung', 1], ['Wocheneinkauf', 1], ['Umzug', 1],
  ['Socken, die einzeln verschwinden', 2], ['Yoga für Katzen', 2], ['Kompost mit Charakter', 2],
  ['Gerüchteküche im Büro', 2], ['Wolkenbeobachtung', 2], ['Schneckenrennen', 2], ['Lieblingsausreden', 2],
  ['Hochzeit mit Hindernissen', 2], ['Mittagsschlaf-Optimierung', 2], ['Verleih von Regenschirmen', 2],
  ['Weltherrschaft für Anfänger', 3], ['Schlafwandel-Navigation', 3], ['Zeitreise-Tickets', 3],
  ['Rückruf-Aktion für Glück', 3], ['Einhorn-Verleih', 3], ['Tauschbörse für Geheimnisse', 3],
  ['Die Kunst des Zuspätkommens', 3], ['Keks-Notfalldienst', 3], ['Wetterbeeinflussung im Kleingarten', 3],
  ['Verwaltung von Superkräften', 3], ['Geisterhaus-Vermietung', 3], ['Streit mit dem Toaster', 3]
]},
{ name: 'Gerät', label: 'GERÄT / FORMAT', icon: 'geraet', items: [
  ['Smartphone, Hochformat (390 × 844)', 1, 390, 844], ['Tablet, Hochformat (768 × 1024)', 1, 768, 1024],
  ['Desktop (1440 × 900)', 1, 1440, 900],
  ['Smartphone, Querformat (844 × 390)', 2, 844, 390], ['Smartwatch (ca. 200 × 240)', 2, 200, 240],
  ['Kiosk-Display, Querformat (1920 × 1080)', 2, 1920, 1080], ['Tablet, Querformat (1024 × 768)', 2, 1024, 768],
  ['Sehr kleines Display (ca. 120 × 120)', 3, 120, 120], ['Riesen-Bildschirm (3840 × 1080)', 3, 3840, 1080]
]},
{ name: 'Hauptaktion', label: 'HAUPTAKTION (ORANGE!)', icon: 'aktion', items: [
  ['Einen Termin buchen', 1], ['Etwas kaufen', 1], ['Sich anmelden', 1], ['Etwas bewerten', 1], ['Etwas teilen', 1],
  ['Eine Nachricht senden', 1], ['Hilfe rufen', 1], ['Etwas zurückgeben', 1], ['Jemanden finden', 1], ['Etwas melden', 1],
  ['Eine Ausrede abschicken', 2], ['Eine Überraschung planen', 2], ['Etwas verleihen', 2],
  ['Einen Streit schlichten', 2], ['Eine Wette abschließen', 2],
  ['Den Alarm auslösen', 3], ['Wolken bestellen', 3], ['Eine Verschwörung melden', 3],
  ['Das Universum neu starten', 3], ['Einen Drachen adoptieren', 3]
]},
{ name: 'Twist', label: 'TWIST', icon: 'twist', items: [
  ['Nur 3 Elemente pro Screen', 1], ['Alles auf einem Screen, ohne Scrollen', 1], ['Ohne Text, nur Symbole', 1],
  ['Die Nutzerin hat nur eine Hand frei', 1], ['Schlechter Empfang: Offline-Zustand mitdenken', 1],
  ['Besonders große Touch-Ziele', 1], ['Hoher Kontrast und große Schrift (barrierearm)', 1],
  ['Erster Besuch, keine Anleitung', 1], ['Fehlerfall mitzeichnen: Was geht schief?', 1],
  ['Die Nutzerin ist wütend', 2], ['In 30 Sekunden bedienbar', 2], ['Navigation nur am unteren Rand', 2],
  ['Alle Inhalte in einer einzigen Liste', 2], ['Zwei wichtige Aktionen, aber nur eine darf orange sein', 2],
  ['Bedienung nur mit dem Ellbogen', 3], ['Das Display ist nass vom Regen', 3], ['Es gibt nur einen einzigen Button', 3],
  ['Die Nutzerin liest nur jedes zweite Wort', 3], ['Bedienung im Dunkeln', 3]
]}

];

/* ———- Zustand ———- */ const S = { sel: [0,0,0,0,0,0], locked: [false,false,false,false,false,false], until: [0,0,0,0,0,0], level: 3, n: 6 }; let hoverIdx = -1, hoverLock = false, host;

/* ———- Würfeln ———- */ function pickIndex(pi) {

const items = POOLS[pi].items;
let cands = [];
items.forEach((it, ix) => { if (it[1] <= S.level) cands.push([ix, it[1] === S.level ? 3 : 1]); });
if (cands.length > 1) cands = cands.filter(c => c[0] !== S.sel[pi]);
const total = cands.reduce((a, c) => a + c[1], 0);
let r = Math.random() * total;
for (const c of cands) { r -= c[1]; if (r <= 0) return c[0]; }
return cands[0][0];

} function rollCard(i, order) {

if (S.locked[i]) return;
S.sel[i] = pickIndex(i);
S.until[i] = millis() + 450 + (order || 0) * 120;
loop();

} function rollAll() { for (let i = 0; i < S.n; i++) rollCard(i, i); updateBrief(); }

/* ———- Layout ———- */ function layout() {

const w = width, cols = w >= 720 ? 3 : (w >= 460 ? 2 : 1), gap = 14;
const cw = (w - gap * (cols - 1)) / cols, ch = cols === 3 ? 172 : (cols === 2 ? 164 : 144);
const rects = [], rows = Math.ceil(S.n / cols);
for (let i = 0; i < S.n; i++) {
  rects.push({ x: (i % cols) * (cw + gap), y: Math.floor(i / cols) * (ch + gap), w: cw, h: ch });
}
return { rects, H: rows * ch + (rows - 1) * gap };

} function applyLayout() {

const w = Math.max(280, host.clientWidth);
resizeCanvas(w, 10, true);
resizeCanvas(w, layout().H);

}

/* ———- Zeichnen ———- */ function wrapLines(str, w) {

const words = str.split(' '), lines = []; let cur = '';
for (const wd of words) {
  const t = cur ? cur + ' ' + wd : wd;
  if (!cur || textWidth(t) <= w) cur = t; else { lines.push(cur); cur = wd; }
}
if (cur) lines.push(cur);
return lines;

} function fit(str, w, h) {

textStyle(BOLD);
for (let s = 28; s >= 14; s--) {
  textSize(s);
  const ls = wrapLines(str, w);
  if (ls.length * s * 1.2 <= h && ls.every(l => textWidth(l) <= w)) return { s, ls };
}
textSize(14);
return { s: 14, ls: wrapLines(str, w) };

} function drawIcon(kind, x, y, item) {

noFill(); stroke(K); strokeWeight(2 / 0.75); // wirkt nach scale(0.75) wie 2 px, wie die Kästchen
if (kind === 'produkt') {
  rect(x, y + 4, 44, 34, 4); line(x, y + 13, x + 44, y + 13);
  noStroke(); fill(O); circle(x + 7, y + 8.5, 4);
} else if (kind === 'kundin') {
  circle(x + 22, y + 13, 16); arc(x + 22, y + 44, 34, 30, PI, TWO_PI);
} else if (kind === 'thema') {
  rect(x, y + 6, 44, 32, 3); line(x, y + 6, x + 44, y + 38); line(x + 44, y + 6, x, y + 38);
} else if (kind === 'geraet') {
  const sc = Math.min(40 / item[2], 40 / item[3]), w = item[2] * sc, h = item[3] * sc;
  rect(x + 22 - w / 2, y + 22 - h / 2, w, h, 3);
} else if (kind === 'aktion') {
  noStroke(); fill(O); rect(x, y + 10, 44, 24, 7);
} else if (kind === 'twist') {
  noStroke(); fill(B); push(); translate(x + 22, y + 22); rotate(PI / 4); rect(-13, -13, 26, 26, 3); pop();
}

} function drawLock(x, y, locked) {

const c = locked ? B : G3;
stroke(c); strokeWeight(2); noFill();
arc(x + 9, y + (locked ? 8 : 4), 12, 14, PI, TWO_PI);
fill(locked ? B : 255); rect(x, y + 8, 18, 13, 3);

} function drawCard(i, r, item, rolling) {

const p = POOLS[i];
// 2 px Kontur, 14 px Radius, 1 px nach innen versetzt: exakt wie das Kästchen „Dein Briefing“
stroke(hoverIdx === i ? O : K); strokeWeight(2); fill(255); rect(r.x + 1, r.y + 1, r.w - 2, r.h - 2, 14);
noStroke(); fill(G3); textStyle(BOLD); textSize(13); textAlign(LEFT, TOP);
text(p.label, r.x + 20, r.y + 16);
push(); translate(r.x + r.w - 20 - 33, r.y + 14); scale(0.75); drawIcon(p.icon, 0, 0, item); pop();
const zone = { x: r.x + 20, y: r.y + 52, w: r.w - 40, h: r.h - 52 - 40 };
const f = fit(item[0], zone.w, zone.h);
textSize(f.s); textStyle(BOLD); noStroke(); fill(rolling ? G2 : K); textAlign(LEFT, TOP);
f.ls.forEach((l, k) => text(l, zone.x, zone.y + k * f.s * 1.2));
// unten: neu würfeln + Schloss
noFill(); stroke(G3); strokeWeight(2); arc(r.x + 26, r.y + r.h - 26, 11, 11, 0.5, TWO_PI - 0.6);
noStroke(); fill(G3); textSize(12); textStyle(NORMAL); textAlign(LEFT, CENTER);
text('neu würfeln', r.x + 40, r.y + r.h - 26);
drawLock(r.x + r.w - 20 - 18, r.y + r.h - 16 - 21, S.locked[i]);

} function setup() {

host = document.getElementById('stage');
const c = createCanvas(Math.max(280, host.clientWidth), 10);
c.parent(host); textFont('Arial'); frameRate(30);
const el = c.elt;
el.addEventListener('click', e => {
  const h = hit(e.offsetX, e.offsetY); if (h.i < 0) return;
  if (h.lock) { S.locked[h.i] = !S.locked[h.i]; redraw(); } else { rollCard(h.i, 0); updateBrief(); }
});
el.addEventListener('mousemove', e => {
  const h = hit(e.offsetX, e.offsetY);
  if (h.i !== hoverIdx || h.lock !== hoverLock) { hoverIdx = h.i; hoverLock = h.lock; el.style.cursor = h.i >= 0 ? 'pointer' : 'default'; redraw(); }
});
el.addEventListener('mouseleave', () => { hoverIdx = -1; hoverLock = false; redraw(); });
window.addEventListener('resize', () => { applyLayout(); });
initFromHash();

} function draw() {

background(255);
const L = layout(), now = millis(); let busy = false;
for (let i = 0; i < S.n; i++) {
  const rolling = now < S.until[i]; if (rolling) busy = true;
  const items = POOLS[i].items;
  const idx = rolling ? Math.floor(Math.abs(Math.sin(Math.floor(now / 70) * 12.9898 + i * 78.233)) * 10000) % items.length : S.sel[i];
  drawCard(i, L.rects[i], items[idx], rolling);
}
if (!busy) noLoop();

} function hit(mx, my) {

const L = layout();
for (let i = 0; i < S.n; i++) {
  const r = L.rects[i];
  if (mx >= r.x && mx <= r.x + r.w && my >= r.y && my <= r.y + r.h) {
    return { i, lock: mx > r.x + r.w - 56 && my > r.y + r.h - 50 };
  }
}
return { i: -1, lock: false };

}

/* ———- Briefing, Teilen ———- */ function briefText() {

const out = [];
for (let i = 0; i < S.n; i++) out.push(POOLS[i].name + ': ' + POOLS[i].items[S.sel[i]][0]);
return out.join('\n');

} function updateBrief() { document.getElementById('brief').textContent = briefText(); } function initFromHash() {

let ok = false;
try {
  const m = location.hash.replace('#', '').split('.');
  if (m.length === 3) {
    const n = parseInt(m[0], 10), l = parseInt(m[1], 10), sel = m[2].split('-').map(x => parseInt(x, 10));
    if ((n === 3 || n === 6) && l >= 1 && l <= 3 && sel.length === 6 &&
        sel.every((v, i) => Number.isInteger(v) && v >= 0 && v < POOLS[i].items.length)) {
      S.n = n; S.level = l; S.sel = sel; ok = true;
    }
  }
} catch (e) {}
syncSeg(); applyLayout();
if (ok) { updateBrief(); } else { rollAll(); }

}

/* ———- Bedienelemente ———- */ function syncSeg() {

document.querySelectorAll('[data-n]').forEach(b => b.setAttribute('aria-pressed', String(+b.dataset.n === S.n)));
document.querySelectorAll('[data-l]').forEach(b => b.setAttribute('aria-pressed', String(+b.dataset.l === S.level)));

} document.getElementById('rollAll').addEventListener('click', rollAll); document.querySelectorAll('[data-n]').forEach(b ⇒ b.addEventListener('click', () ⇒ {

S.n = +b.dataset.n; syncSeg(); applyLayout(); updateBrief();

})); document.querySelectorAll('[data-l]').forEach(b ⇒ b.addEventListener('click', () ⇒ {

S.level = +b.dataset.l; syncSeg(); rollAll();

})); async function copyText(text) {

try { await navigator.clipboard.writeText(text); }
catch (e) {
  const ta = document.createElement('textarea'); ta.value = text; document.body.appendChild(ta); ta.select();
  try { document.execCommand('copy'); } catch (_) {}
  ta.remove();
}

} document.getElementById('copyTxt').addEventListener('click', async () ⇒ {

await copyText(briefText());
const a = document.getElementById('icCopy'), b = document.getElementById('icDone');
a.style.display = 'none'; b.style.display = 'block';
setTimeout(() => { a.style.display = 'block'; b.style.display = 'none'; }, 1400);

});

/* ———- Timer ———- */ let tTotal = 600, tLeft = 600, tEnd = 0, tTimer = null; const timeEl = document.getElementById('time'), startBtn = document.getElementById('tStart'); function fmt(s) { return String(Math.floor(s / 60)).padStart(2, '0') + ':' + String(s % 60).padStart(2, '0'); } function showTime() {

timeEl.textContent = tLeft > 0 ? fmt(tLeft) : 'Zeit!';
timeEl.classList.toggle('done', tLeft <= 0);

} function tick() {

tLeft = Math.max(0, Math.ceil((tEnd - Date.now()) / 1000)); showTime();
if (tLeft <= 0) { clearInterval(tTimer); tTimer = null; startBtn.textContent = 'Start'; }

} startBtn.addEventListener('click', () ⇒ {

if (tTimer) { clearInterval(tTimer); tTimer = null; startBtn.textContent = 'Weiter'; return; }
if (tLeft <= 0) tLeft = tTotal;
tEnd = Date.now() + tLeft * 1000; tTimer = setInterval(tick, 250); startBtn.textContent = 'Pause'; tick();

}); function resetTimer() {

if (tTimer) { clearInterval(tTimer); tTimer = null; }
tLeft = tTotal; startBtn.textContent = 'Start'; showTime();

} document.getElementById('tReset').addEventListener('click', resetTimer); document.querySelectorAll('[data-min]').forEach(b ⇒ b.addEventListener('click', () ⇒ {

tTotal = +b.dataset.min * 60;
document.querySelectorAll('[data-min]').forEach(x => x.setAttribute('aria-pressed', String(x === b)));
resetTimer();

})); </script> </body> </html>

This website uses cookies. By using our website, you agree with storing cookies on your computer. Also you acknowledge that you have read and understand our Privacy Policy. If you do not agree leave the website. Privacy Policy
/var/www/vhosts/ct-lab.info/wiki.ct-lab.info/data/attic/extras/codikon/p5js/wireframe-ideen-generator.1793003204.txt.gz · Last modified: by Leonie Winkelmann