This commit is contained in:
Felix Klement committed 2026-10-03 19:35:22 +02:00
1 parent c8740fb1d3
commit c3f4651e71
11 files changed
+3827 -2

No files matched your search

Binary file not shown.

Before

Width:  |  Height:  |  Size: 549 KiB

After

Width:  |  Height:  |  Size: 370 KiB

+332
View File
@@ -0,0 +1,332 @@
---
import data from '../../data/research.json';
import { t, type Lang } from '../../i18n/ui';
import type { ChladniParams } from '../../scripts/chladni';
interface Props { lang: Lang }
const { lang } = Astro.props;
const ui = t(lang);
const r = ui.team.research;
/* Papers from both founders' Google Scholar profiles, deduplicated (src/data/research.json).
Rendered most cited first, so the page reads right without script; the script below re-sorts,
filters and folds the list. Entries marked display: false stay in the data only. */
const papers = data.papers.filter((p) => p.display && p.href);
const categories = data.categories.filter((c) => papers.some((p) => p.categories.includes(c)));
const countIn = (c: string) => papers.filter((p) => p.categories.includes(c)).length;
const INITIAL = 9;
const labels = { showAll: r.showAll(papers.length), showAllTpl: r.showAll(999).replace('999', '{n}'), showFewer: r.showFewer, count: r.count(111, 999).replace('111', '{shown}').replace('999', '{total}') };
/* Every paper carries its own figure (Chladni parameters for src/scripts/chladni.ts) and colour
field (three tokens and their positions) in research.json, generated per paper by
scripts/research-figures.py. They travel with the paper, so sorting and filtering never move them. */
const figure = (paper: (typeof papers)[number]) => ({ quality: 121, ...paper.figure }) as ChladniParams;
const fieldStyle = (paper: (typeof papers)[number]) =>
paper.field.colors.map((c, k) => `--c${k + 1}: var(--${c}); --ax${k + 1}: ${paper.field.positions[k][0]}%; --ay${k + 1}: ${paper.field.positions[k][1]}%;`).join(' ');
---
<section class="section research" aria-labelledby="research-title">
<div class="container">
<div class="grid research__head">
<h2 id="research-title" class="h2 research__title">
{ui.team.research.title.map((line) => <span class="research__line">{line}</span>)}
</h2>
<p class="research__intro muted">{ui.team.research.intro}</p>
</div>
<div class="research__controls" data-research-controls hidden>
<fieldset class="research__group">
<legend class="research__legend">{r.sortLabel}</legend>
<div class="research__options">
{(['cites', 'newest', 'oldest'] as const).map((key, i) => (
<label class="research__option">
<input type="radio" name="research-sort" value={key} checked={i === 0} />
<span>{r.sort[key]}</span>
</label>
))}
</div>
</fieldset>
<fieldset class="research__group">
<legend class="research__legend">{r.filterLabel}</legend>
<div class="research__options research__options--wrap">
<label class="research__option">
<input type="radio" name="research-category" value="" checked />
<span>{r.all} <span class="research__n">{papers.length}</span></span>
</label>
{categories.map((c) => (
<label class="research__option">
<input type="radio" name="research-category" value={c} />
<span>{r.categories[c] ?? c} <span class="research__n">{countIn(c)}</span></span>
</label>
))}
</div>
</fieldset>
</div>
<ul class="research__list" role="list" data-research-list data-initial={INITIAL}>
{papers.map((paper, i) => (
<li
class="research__item"
data-cites={paper.cites}
data-date={paper.date ?? String(paper.year)}
data-order={i}
data-categories={paper.categories.join('|')}
>
<a class="paper" href={paper.href} rel="noopener" lang="en">
<div class="paper__field" style={fieldStyle(paper)}>
<canvas class="paper__lines" aria-hidden="true" data-chladni={JSON.stringify(figure(paper))}></canvas>
<span class="paper__year">{paper.year}</span>
</div>
<div class="paper__body">
<h3 class="paper__title">{paper.title}</h3>
<p class="paper__venue small muted">{paper.venue}</p>
<div class="paper__meta small">
<span class="muted" lang={lang}>{paper.categories.map((c) => r.categories[c] ?? c).join(', ')}</span>
</div>
</div>
</a>
</li>
))}
</ul>
<div class="research__foot">
<p class="small muted" aria-live="polite" data-research-count></p>
<button class="research__more" type="button" data-research-more hidden data-labels={JSON.stringify(labels)}>{labels.showAll}</button>
</div>
</div>
</section>
<style>
/* Registered so the field positions can transition on hover. */
@property --x1 { syntax: '<percentage>'; inherits: false; initial-value: 50%; }
@property --y1 { syntax: '<percentage>'; inherits: false; initial-value: 50%; }
@property --x2 { syntax: '<percentage>'; inherits: false; initial-value: 50%; }
@property --y2 { syntax: '<percentage>'; inherits: false; initial-value: 50%; }
@property --x3 { syntax: '<percentage>'; inherits: false; initial-value: 50%; }
@property --y3 { syntax: '<percentage>'; inherits: false; initial-value: 50%; }
.research__line { display: block; }
.research__title { grid-column: 1 / -1; }
.research__intro { grid-column: 1 / -1; max-width: var(--measure); }
@media (min-width: 48rem) {
.research__title { grid-column: 1 / span 7; }
.research__intro { grid-column: 9 / span 4; align-self: end; }
}
.research__list {
margin-top: var(--space-8);
display: grid;
gap: var(--gap);
}
@media (min-width: 40rem) { .research__list { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (min-width: 64rem) { .research__list { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
.paper {
display: grid;
grid-template-rows: auto 1fr;
height: 100%;
border: var(--hairline) solid var(--line);
text-decoration: none;
transition: border-color var(--dur-fast) var(--ease);
}
.paper:hover { border-color: var(--ink-muted); }
.paper:hover .paper__title {
text-decoration: underline;
text-decoration-thickness: var(--hairline);
text-underline-offset: 0.14em;
}
.paper__field {
--x1: var(--ax1); --y1: var(--ay1);
--x2: var(--ax2); --y2: var(--ay2);
--x3: var(--ax3); --y3: var(--ay3);
position: relative;
aspect-ratio: 316 / 170;
overflow: hidden;
background-color: var(--night);
background-image:
radial-gradient(80% 110% at var(--x1) var(--y1), var(--c1), transparent 70%),
radial-gradient(70% 100% at var(--x2) var(--y2), var(--c2), transparent 68%),
radial-gradient(95% 130% at var(--x3) var(--y3), var(--c3), transparent 72%);
transition:
--x1 var(--dur-slow) var(--ease), --y1 var(--dur-slow) var(--ease),
--x2 var(--dur-slow) var(--ease), --y2 var(--dur-slow) var(--ease),
--x3 var(--dur-slow) var(--ease), --y3 var(--dur-slow) var(--ease);
}
/* Hover answers the pointer: the fields drift a little, nothing lifts or glows. */
.paper:hover .paper__field {
--x1: calc(var(--ax1) + 7%); --y1: calc(var(--ay1) - 6%);
--x2: calc(var(--ax2) - 6%); --y2: calc(var(--ay2) + 7%);
--x3: calc(var(--ax3) + 5%); --y3: calc(var(--ay3) + 5%);
}
/* Film grain over the colour. */
.paper__field::before {
content: '';
position: absolute;
inset: 0;
pointer-events: none;
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='2' stitchTiles='stitch'/%3E%3CfeColorMatrix values='0 0 0 0 1 0 0 0 0 1 0 0 0 0 1 0 0 0 0.55 0'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
mix-blend-mode: overlay;
opacity: 0.6;
}
/* The Chladni lines: a 3:1 canvas centred on the field and cropped by it, so the figure keeps
its proportions at every card width. Stroke colour comes from `color`. */
.paper__lines {
position: absolute;
top: 0;
left: 50%;
height: 100%;
aspect-ratio: 3 / 1;
transform: translateX(-50%);
color: color-mix(in srgb, var(--paper) 72%, transparent);
pointer-events: none;
}
.paper__year {
position: absolute;
top: var(--space-3);
left: var(--space-3);
z-index: 1;
padding: var(--space-1) var(--space-2);
background: var(--paper);
color: var(--ink);
font-family: var(--font-mono);
font-size: var(--step-micro);
font-weight: var(--weight-medium);
letter-spacing: var(--track-caps);
line-height: 1;
}
.paper__body {
display: grid;
align-content: space-between;
gap: var(--space-4);
padding: var(--space-4);
}
.paper__title {
font-size: var(--step-body);
font-weight: var(--weight-medium);
line-height: var(--leading-small);
letter-spacing: 0;
}
/* Controls: segmented rows in the menu bar's language, hairline cells and mono caps. */
.research__controls {
margin-top: var(--space-7);
display: grid;
gap: var(--space-4);
}
.research__group { border: 0; padding: 0; margin: 0; min-width: 0; display: grid; gap: var(--space-2); }
.research__legend { padding: 0; color: var(--ink-muted); font-size: var(--step-small); }
/* Each option draws its own frame and overlaps its neighbours by one line, so a wrapped row
never leaves a rule running past its last option. */
.research__options { display: flex; flex-wrap: wrap; padding: 0 var(--hairline) var(--hairline) 0; }
.research__option { position: relative; display: flex; margin: 0 calc(-1 * var(--hairline)) calc(-1 * var(--hairline)) 0; }
.research__option input { position: absolute; opacity: 0; inset: 0; margin: 0; cursor: pointer; }
.research__option > span {
display: flex;
align-items: center;
gap: var(--space-2);
padding: var(--space-3) var(--space-4);
border: var(--hairline) solid var(--line);
font-family: var(--font-mono);
font-size: var(--step-micro);
font-weight: var(--weight-medium);
letter-spacing: var(--track-caps);
text-transform: uppercase;
line-height: 1;
transition: background-color var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease);
}
.research__option:hover > span { background: color-mix(in srgb, var(--ink) var(--tint-hover), transparent); }
.research__option input:checked + span { background: var(--ink); color: var(--paper); }
.research__option input:focus-visible + span { outline: var(--focus-width) solid var(--ink); outline-offset: calc(-1 * var(--focus-width) - 2px); }
.research__n { color: inherit; opacity: 0.6; }
.research__list { margin-top: var(--space-6); scroll-margin-top: calc(var(--header-height) + var(--space-5)); }
.research__item[hidden] { display: none; }
.paper__meta {
padding-top: var(--space-3);
border-top: var(--hairline) solid var(--line);
}
.research__foot {
margin-top: var(--space-6);
display: flex;
flex-wrap: wrap;
align-items: center;
gap: var(--space-4) var(--space-6);
}
.research__more {
padding: var(--space-3) var(--space-5);
border: var(--hairline) solid var(--ink);
font-family: var(--font-mono);
font-size: var(--step-micro);
font-weight: var(--weight-medium);
letter-spacing: var(--track-caps);
text-transform: uppercase;
transition: background-color var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease);
}
.research__more:hover { background: var(--ink); color: var(--paper); }
</style>
<script>
import { mountChladni, type ChladniParams } from '../../scripts/chladni';
const reduce = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
/* Figures draw when their card comes near the viewport: 39 canvases, most never seen. */
const lazy = new IntersectionObserver((entries) => {
for (const entry of entries) {
if (!entry.isIntersecting) continue;
const canvas = entry.target as HTMLCanvasElement;
lazy.unobserve(canvas);
const card = canvas.closest<HTMLElement>('.paper');
if (card) mountChladni(canvas, card, JSON.parse(canvas.dataset.chladni!) as ChladniParams, reduce);
}
}, { rootMargin: '300px 0px' });
document.querySelectorAll<HTMLCanvasElement>('[data-chladni]').forEach((c) => lazy.observe(c));
/* Sort, filter, fold. The server renders every card most cited first; this only re-orders and
hides list items, so links, figures and focus stay intact. */
const list = document.querySelector<HTMLElement>('[data-research-list]');
const controls = document.querySelector<HTMLElement>('[data-research-controls]');
const more = document.querySelector<HTMLButtonElement>('[data-research-more]');
const count = document.querySelector<HTMLElement>('[data-research-count]');
if (list && controls && more && count) {
const items = [...list.querySelectorAll<HTMLElement>('.research__item')];
const initial = Number(list.dataset.initial) || 9;
const labels = JSON.parse(more.dataset.labels!) as { showAllTpl: string; showFewer: string; count: string };
let expanded = false;
controls.hidden = false;
const value = (name: string) => controls.querySelector<HTMLInputElement>(`input[name="${name}"]:checked`)?.value ?? '';
const compare: Record<string, (a: HTMLElement, b: HTMLElement) => number> = {
cites: (a, b) => Number(b.dataset.cites) - Number(a.dataset.cites) || Number(a.dataset.order) - Number(b.dataset.order),
newest: (a, b) => (b.dataset.date ?? '').localeCompare(a.dataset.date ?? '') || Number(b.dataset.cites) - Number(a.dataset.cites),
oldest: (a, b) => (a.dataset.date ?? '').localeCompare(b.dataset.date ?? '') || Number(b.dataset.cites) - Number(a.dataset.cites),
};
const render = () => {
const sort = value('research-sort') || 'cites';
const category = value('research-category');
const matching = items.filter((li) => !category || li.dataset.categories!.split('|').includes(category));
matching.sort(compare[sort]);
const visible = expanded ? matching : matching.slice(0, initial);
for (const li of items) li.hidden = true;
for (const li of matching) { list.append(li); }
for (const li of visible) li.hidden = false;
count.textContent = labels.count.replace('{shown}', String(visible.length)).replace('{total}', String(matching.length));
more.hidden = matching.length <= initial;
more.textContent = expanded ? labels.showFewer : labels.showAllTpl.replace('{n}', String(matching.length));
more.setAttribute('aria-expanded', String(expanded));
};
controls.addEventListener('change', () => { expanded = false; render(); });
more.addEventListener('click', () => {
expanded = !expanded;
render();
if (!expanded) list.scrollIntoView({ block: 'start' });
});
render();
}
</script>
File diff suppressed because it is too large. Load diff
+198
View File
@@ -0,0 +1,198 @@
{
"profile": "https://scholar.google.com/citations?user=MRMV2cwAAAAJ&hl=en",
"fetchedAt": "2026-10-03",
"entries": [
{
"title": "Open or not open: Are conventional radio access networks more secure and trustworthy than Open-RAN?",
"authors": "F Klement, S Katzenbeisser, V Ulitzsch, J Krämer, S Stanczak, Z Utkovski, ...",
"venue": "arXiv preprint arXiv:2204.12227, 2022",
"cites": 35,
"year": 2022,
"scholarUrl": "https://scholar.google.com/citations?view_op=view_citation&hl=en&user=MRMV2cwAAAAJ&pagesize=100&citation_for_view=MRMV2cwAAAAJ:d1gkVwhDpl0C"
},
{
"title": "Toward securing the 6G transition: A comprehensive empirical method to analyze threats in O-RAN environments",
"authors": "F Klement, W Liu, S Katzenbeisser",
"venue": "IEEE Journal on Selected Areas in Communications 42 (2), 420-431, 2023",
"cites": 29,
"year": 2023,
"scholarUrl": "https://scholar.google.com/citations?view_op=view_citation&hl=en&user=MRMV2cwAAAAJ&pagesize=100&citation_for_view=MRMV2cwAAAAJ:W7OEmFMy1HYC"
},
{
"title": "Securing the open ran infrastructure: Exploring vulnerabilities in kubernetes deployments",
"authors": "F Klement, A Brighente, M Polese, M Conti, S Katzenbeisser",
"venue": "2024 IEEE 10th International Conference on Network Softwarization (NetSoft …, 2024",
"cites": 21,
"year": 2024,
"scholarUrl": "https://scholar.google.com/citations?view_op=view_citation&hl=en&user=MRMV2cwAAAAJ&pagesize=100&citation_for_view=MRMV2cwAAAAJ:ufrVoPGSRksC"
},
{
"title": "Man-in-the-OBD: A modular, protocol agnostic firewall for automotive dongles to enhance privacy and security",
"authors": "F Klement, HC Pöhls, S Katzenbeisser",
"venue": "International Workshop on Attacks and Defenses for Internet-of-Things, 143-164, 2022",
"cites": 13,
"year": 2022,
"scholarUrl": "https://scholar.google.com/citations?view_op=view_citation&hl=en&user=MRMV2cwAAAAJ&pagesize=100&citation_for_view=MRMV2cwAAAAJ:9yKSN-GCB0IC"
},
{
"title": "Endless subscriptions: open RAN is open to RIC E2 subscription denial of service attacks",
"authors": "F Klement, A Brighente, AK Bhattacharjee, S Cecconello, F Kuipers, ...",
"venue": "2025 IEEE 10th European Symposium on Security and Privacy (EuroS&P), 755-770, 2025",
"cites": 11,
"year": 2025,
"scholarUrl": "https://scholar.google.com/citations?view_op=view_citation&hl=en&user=MRMV2cwAAAAJ&pagesize=100&citation_for_view=MRMV2cwAAAAJ:Se3iqnhoufwC"
},
{
"title": "Segmentation and Filtering Are Still the Gold Standard for Privacy in IoT—An In-Depth STRIDE and LINDDUN Analysis of Smart Homes",
"authors": "HC Pöhls, F Kügler, E Geloczi, F Klement",
"venue": "Future Internet 17 (2), 77, 2025",
"cites": 9,
"year": 2025,
"scholarUrl": "https://scholar.google.com/citations?view_op=view_citation&hl=en&user=MRMV2cwAAAAJ&pagesize=100&citation_for_view=MRMV2cwAAAAJ:roLk4NBRz8UC"
},
{
"title": "Testing physical unclonable functions implemented on commercial off-the-shelf NAND flash memories using programming disturbances",
"authors": "NA Anagnostopoulos, Y Fan, MU Saleem, N Mexis, E Gelóczi, F Klement, ...",
"venue": "2022 IEEE 12th International Conference on Consumer Electronics (ICCE-Berlin …, 2022",
"cites": 8,
"year": 2022,
"scholarUrl": "https://scholar.google.com/citations?view_op=view_citation&hl=en&user=MRMV2cwAAAAJ&pagesize=100&citation_for_view=MRMV2cwAAAAJ:UeHWp8X0CEIC"
},
{
"title": "Change Your Car's Filters: Efficient Concurrent and Multi-Stage Firewall for OBD-II Network Traffic",
"authors": "F Klement, HC Pöhls, S Katzenbeisser",
"venue": "2022 IEEE 27th International Workshop on Computer Aided Modeling and Design …, 2022",
"cites": 7,
"year": 2022,
"scholarUrl": "https://scholar.google.com/citations?view_op=view_citation&hl=en&user=MRMV2cwAAAAJ&pagesize=100&citation_for_view=MRMV2cwAAAAJ:qjMakFHDy7sC"
},
{
"title": "Secure Data-Binding in FPGA-based Hardware Architectures utilizing PUFs",
"authors": "F Frank, M Schmid, F Klement, P Palani, A Weber, EB Kavun, W Xiong, ...",
"venue": "Proceedings of the 19th ACM Asia Conference on Computer and Communications …, 2024",
"cites": 5,
"year": 2024,
"scholarUrl": "https://scholar.google.com/citations?view_op=view_citation&hl=en&user=MRMV2cwAAAAJ&pagesize=100&citation_for_view=MRMV2cwAAAAJ:_FxGoFyzp5QC"
},
{
"title": "One standard to rule them all? Assessing the disruptive potential of jamming attacks on Matter networks",
"authors": "F Klement, E Vorderwülbeke, S Katzenbeisser",
"venue": "2023 IEEE International Workshop on Information Forensics and Security (WIFS …, 2023",
"cites": 5,
"year": 2023,
"scholarUrl": "https://scholar.google.com/citations?view_op=view_citation&hl=en&user=MRMV2cwAAAAJ&pagesize=100&citation_for_view=MRMV2cwAAAAJ:YsMSGLbcyi4C"
},
{
"title": "Ensuring trustworthy automated road vehicles: A software integrity validation approach",
"authors": "D Püllen, F Klement, A Vinel, S Katzenbeisser",
"venue": "2023 IEEE International Automated Vehicle Validation Conference (IAVVC), 1-8, 2023",
"cites": 5,
"year": 2023,
"scholarUrl": "https://scholar.google.com/citations?view_op=view_citation&hl=en&user=MRMV2cwAAAAJ&pagesize=100&citation_for_view=MRMV2cwAAAAJ:Y0pCki6q_DkC"
},
{
"title": "SoK: delegated Security in the Internet of Things",
"authors": "E Geloczi, F Klement, P Struck, S Katzenbeisser",
"venue": "Future Internet 17 (5), 202, 2025",
"cites": 4,
"year": 2025,
"scholarUrl": "https://scholar.google.com/citations?view_op=view_citation&hl=en&user=MRMV2cwAAAAJ&pagesize=100&citation_for_view=MRMV2cwAAAAJ:UebtZRa9Y70C"
},
{
"title": "Unveiling the shadows: An approach towards detection, precise localization, and effective isolation of concealed IoT devices in unfamiliar environments",
"authors": "E Geloczi, HC Pöhls, F Klement, J Posegga, S Katzenbeisser",
"venue": "Proceedings of the 23rd Workshop on Privacy in the Electronic Society, 109-123, 2023",
"cites": 4,
"year": 2023,
"scholarUrl": "https://scholar.google.com/citations?view_op=view_citation&hl=en&user=MRMV2cwAAAAJ&pagesize=100&citation_for_view=MRMV2cwAAAAJ:LkGwnXOMwfcC"
},
{
"title": "Form Without Function: Agent Social Behavior in the Moltbook Network",
"authors": "S Zerhoudi, KG Dastidar, F Klement, A Romazanov, A Einwiller, DH Dang, ...",
"venue": "arXiv preprint arXiv:2604.13052, 2026",
"cites": 3,
"year": 2026,
"scholarUrl": "https://scholar.google.com/citations?view_op=view_citation&hl=en&user=MRMV2cwAAAAJ&pagesize=100&citation_for_view=MRMV2cwAAAAJ:3fE2CSJIrl8C"
},
{
"title": "Private and secure over-the-air multi-party communication",
"authors": "JJ Brune, M Frey, F Klement, I Bjelaković, S Katzenbeisser, S Stańczak",
"venue": "2023 57th Asilomar Conference on Signals, Systems, and Computers, 1697-1701, 2023",
"cites": 3,
"year": 2023,
"scholarUrl": "https://scholar.google.com/citations?view_op=view_citation&hl=en&user=MRMV2cwAAAAJ&pagesize=100&citation_for_view=MRMV2cwAAAAJ:WF5omc3nYNoC"
},
{
"title": "ORCA - An Automated Threat Analysis Pipeline for O-RAN Continuous Development",
"authors": "F Klement, A Brighente, M Polese, M Conti, S Katzenbeisser",
"venue": "arXiv preprint arXiv:2601.13681, 2026",
"cites": 2,
"year": 2026,
"scholarUrl": "https://scholar.google.com/citations?view_op=view_citation&hl=en&user=MRMV2cwAAAAJ&pagesize=100&citation_for_view=MRMV2cwAAAAJ:8k81kl-MbHgC"
},
{
"title": "FlexRIC Subscription DoS Attack",
"authors": "F Klement",
"venue": "",
"cites": 2,
"year": 2025,
"scholarUrl": "https://scholar.google.com/citations?view_op=view_citation&hl=en&user=MRMV2cwAAAAJ&pagesize=100&citation_for_view=MRMV2cwAAAAJ:5nxA0vEk-isC"
},
{
"title": "Secure Stitch: Unveiling the Fabric of Security Patterns for the Internet of Things",
"authors": "E Geloczi, F Klement, E Gründinger, S Katzenbeisser",
"venue": "International Workshop on Security and Trust Management, 65-84, 2023",
"cites": 2,
"year": 2023,
"scholarUrl": "https://scholar.google.com/citations?view_op=view_citation&hl=en&user=MRMV2cwAAAAJ&pagesize=100&citation_for_view=MRMV2cwAAAAJ:zYLM7Y9cAGgC"
},
{
"title": "Keep your Enemies closer: On the minimal Distance of Adversaries when using Channel-based Key Extraction in SISO 6G Systems",
"authors": "F Klement, S Takebuchi, T Arul, S Katzenbeisser",
"venue": "2023 19th International Conference on Wireless and Mobile Computing …, 2023",
"cites": 2,
"year": 2023,
"scholarUrl": "https://scholar.google.com/citations?view_op=view_citation&hl=en&user=MRMV2cwAAAAJ&pagesize=100&citation_for_view=MRMV2cwAAAAJ:IjCSPb-OGe4C"
},
{
"title": "Enhancing Scalability in Smart Device Networks: A Modular Adaptive IoT Framework for Efficient Resource Allocation and System Growth",
"authors": "M Waseem, F Klement",
"venue": "",
"cites": 1,
"year": 2024,
"scholarUrl": "https://scholar.google.com/citations?view_op=view_citation&hl=en&user=MRMV2cwAAAAJ&pagesize=100&citation_for_view=MRMV2cwAAAAJ:0EnyYjriUFMC"
},
{
"title": "O-RAN Security: From Standardization and Threat Modeling to Practical Security Testing",
"authors": "J Moore, AS Abdalla, F Klement, AR Shakya, P Shantharama, V Marojevic",
"venue": "IEEE Open Journal of the Communications Society, 2026",
"cites": 0,
"year": 2026,
"scholarUrl": "https://scholar.google.com/citations?view_op=view_citation&hl=en&user=MRMV2cwAAAAJ&pagesize=100&citation_for_view=MRMV2cwAAAAJ:KlAtU1dfN6UC"
},
{
"title": "6G Research and Innovation Cluster (6G-RIC)-Offene und sichere 6G-Technologien: Weltmarktchance für Deutschland",
"authors": "F Klement, S Katzenbeisser",
"venue": "Hannover: Technische Informationsbibliothek, 2026",
"cites": 0,
"year": 2026,
"scholarUrl": "https://scholar.google.com/citations?view_op=view_citation&hl=en&user=MRMV2cwAAAAJ&pagesize=100&citation_for_view=MRMV2cwAAAAJ:kNdYIx-mwKoC"
},
{
"title": "Strengthening Security Foundations in Next-G Wireless Telecommunication Systems",
"authors": "F Klement",
"venue": "Universität Passau, 2025",
"cites": 0,
"year": 2025,
"scholarUrl": "https://scholar.google.com/citations?view_op=view_citation&hl=en&user=MRMV2cwAAAAJ&pagesize=100&citation_for_view=MRMV2cwAAAAJ:hqOjcs7Dif8C"
},
{
"title": "1. Felix Klement, Korbinian Spielvogel, Henrich C. Pöhls (UP) 2. Konstantinos Fysarakis, Manolis Chatzimpyrros, Thodoris Galousis, Michalis Smyrlis (STS), 3. Nikolaos …",
"authors": "MF ST",
"venue": "",
"cites": 0,
"year": null,
"scholarUrl": "https://scholar.google.com/citations?view_op=view_citation&hl=en&user=MRMV2cwAAAAJ&pagesize=100&citation_for_view=MRMV2cwAAAAJ:u-x6o8ySG0sC"
}
]
}
+174
View File
@@ -0,0 +1,174 @@
{
"profile": "https://scholar.google.com/citations?user=o9Gu_FMAAAAJ&hl=en",
"fetchedAt": "2026-10-03",
"entries": [
{
"title": "A security process for the automotive service-oriented software architecture",
"authors": "D Püllen, F Frank, M Christl, W Liu, S Katzenbeisser",
"venue": "IEEE Transactions on Vehicular Technology 73 (4), 5036-5053, 2023",
"cites": 15,
"year": 2023,
"scholarUrl": "https://scholar.google.com/citations?view_op=view_citation&hl=en&user=o9Gu_FMAAAAJ&pagesize=100&citation_for_view=o9Gu_FMAAAAJ:LkGwnXOMwfcC"
},
{
"title": "Spatial correlation in weak physical unclonable functions: A comprehensive overview",
"authors": "N Mexis, T Arul, NA Anagnostopoulos, F Frank, S Böttger, M Hartmann, ...",
"venue": "2023 26th Euromicro Conference on Digital System Design (DSD), 70-78, 2023",
"cites": 14,
"year": 2023,
"scholarUrl": "https://scholar.google.com/citations?view_op=view_citation&hl=en&user=o9Gu_FMAAAAJ&pagesize=100&citation_for_view=o9Gu_FMAAAAJ:roLk4NBRz8UC"
},
{
"title": "Using memristor arrays as physical unclonable functions",
"authors": "F Frank, T Arul, NA Anagnostopoulos, S Katzenbeisser",
"venue": "European Symposium on Research in Computer Security, 250-271, 2022",
"cites": 10,
"year": 2022,
"scholarUrl": "https://scholar.google.com/citations?view_op=view_citation&hl=en&user=o9Gu_FMAAAAJ&pagesize=100&citation_for_view=o9Gu_FMAAAAJ:IjCSPb-OGe4C"
},
{
"title": "CNT-PUFs: Highly robust physical unclonable functions based on carbon nanotubes",
"authors": "S Böttger, F Frank, NA Anagnostopoulos, A Mohamed, M Hartmann, ...",
"venue": "2023 IEEE 23rd International Conference on Nanotechnology (NANO), 1-6, 2023",
"cites": 9,
"year": 2023,
"scholarUrl": "https://scholar.google.com/citations?view_op=view_citation&hl=en&user=o9Gu_FMAAAAJ&pagesize=100&citation_for_view=o9Gu_FMAAAAJ:eQOLeE2rZwMC"
},
{
"title": "Testing physical unclonable functions implemented on commercial off-the-shelf NAND flash memories using programming disturbances",
"authors": "NA Anagnostopoulos, Y Fan, MU Saleem, N Mexis, E Gelóczi, F Klement, ...",
"venue": "2022 IEEE 12th International Conference on Consumer Electronics (ICCE-Berlin …, 2022",
"cites": 8,
"year": 2022,
"scholarUrl": "https://scholar.google.com/citations?view_op=view_citation&hl=en&user=o9Gu_FMAAAAJ&pagesize=100&citation_for_view=o9Gu_FMAAAAJ:Tyk-4Ss8FVUC"
},
{
"title": "Abusing commodity DRAMs in IoT devices to remotely spy on temperature",
"authors": "F Frank, W Xiong, NA Anagnostopoulos, A Schaller, T Arul, F Koushanfar, ...",
"venue": "IEEE Transactions on Information Forensics and Security 18, 2991-3005, 2023",
"cites": 7,
"year": 2023,
"scholarUrl": "https://scholar.google.com/citations?view_op=view_citation&hl=en&user=o9Gu_FMAAAAJ&pagesize=100&citation_for_view=o9Gu_FMAAAAJ:Y0pCki6q_DkC"
},
{
"title": "Investigation of Commercial Off-The-Shelf ReRAM Modules for Use as Runtime-Accessible TRNG",
"authors": "T Arul, N Mexis, AE George, F Frank, NA Anagnostopoulos, ...",
"venue": "2024 27th Euromicro Conference on Digital System Design (DSD), 33-42, 2024",
"cites": 5,
"year": 2024,
"scholarUrl": "https://scholar.google.com/citations?view_op=view_citation&hl=en&user=o9Gu_FMAAAAJ&pagesize=100&citation_for_view=o9Gu_FMAAAAJ:3fE2CSJIrl8C"
},
{
"title": "Secure Data-Binding in FPGA-based Hardware Architectures utilizing PUFs",
"authors": "F Frank, M Schmid, F Klement, P Palani, A Weber, EB Kavun, W Xiong, ...",
"venue": "Proceedings of the 19th ACM Asia Conference on Computer and Communications …, 2024",
"cites": 5,
"year": 2024,
"scholarUrl": "https://scholar.google.com/citations?view_op=view_citation&hl=en&user=o9Gu_FMAAAAJ&pagesize=100&citation_for_view=o9Gu_FMAAAAJ:UebtZRa9Y70C"
},
{
"title": "CNT-PUFs: highly robust and heat-tolerant carbon-nanotube-based physical unclonable functions",
"authors": "F Frank, S Böttger, N Mexis, NA Anagnostopoulos, A Mohamed, ...",
"venue": "Nanomaterials 13 (22), 2930, 2023",
"cites": 4,
"year": 2023,
"scholarUrl": "https://scholar.google.com/citations?view_op=view_citation&hl=en&user=o9Gu_FMAAAAJ&pagesize=100&citation_for_view=o9Gu_FMAAAAJ:YsMSGLbcyi4C"
},
{
"title": "Secure communication via chaotic cryptography",
"authors": "E Gelóczi, N Mexis, NA Anagnostopoulos, F Frank, T Arul, SG Stavrinides, ...",
"venue": "crypto day matters 34, 2022",
"cites": 3,
"year": 2022,
"scholarUrl": "https://scholar.google.com/citations?view_op=view_citation&hl=en&user=o9Gu_FMAAAAJ&pagesize=100&citation_for_view=o9Gu_FMAAAAJ:9yKSN-GCB0IC"
},
{
"title": "Fuzzy extractors using low-density parity-check codes",
"authors": "N Mexis, NA Anagnostopoulos, T Arul, F Frank, S Katzenbeisser",
"venue": "crypto day matters 33, 2021",
"cites": 3,
"year": 2021,
"scholarUrl": "https://scholar.google.com/citations?view_op=view_citation&hl=en&user=o9Gu_FMAAAAJ&pagesize=100&citation_for_view=o9Gu_FMAAAAJ:d1gkVwhDpl0C"
},
{
"title": "Real-world chaos-based cryptography using synchronised Chua chaotic circuits",
"authors": "E Nazarenko, NA Anagnostopoulos, SG Stavrinides, N Mexis, F Frank, ...",
"venue": "TechRxiv 2023 (0717), 2023",
"cites": 2,
"year": 2023,
"scholarUrl": "https://scholar.google.com/citations?view_op=view_citation&hl=en&user=o9Gu_FMAAAAJ&pagesize=100&citation_for_view=o9Gu_FMAAAAJ:UeHWp8X0CEIC"
},
{
"title": "A reconfigurable hardware infrastructure for evaluating PUFs in non-volatile memories",
"authors": "F Frank, S Katzenbeisser",
"venue": "HardwareX, e00809, 2026",
"cites": 1,
"year": 2026,
"scholarUrl": "https://scholar.google.com/citations?view_op=view_citation&hl=en&user=o9Gu_FMAAAAJ&pagesize=100&citation_for_view=o9Gu_FMAAAAJ:KlAtU1dfN6UC"
},
{
"title": "Towards Deterministic DDS Communication for Secure Service-Oriented Software-Defined Vehicles",
"authors": "F Frank, D Püllen, A Kampmann, S Katzenbeisser",
"venue": "International Conference on Availability, Reliability and Security, 186-208, 2025",
"cites": 1,
"year": 2025,
"scholarUrl": "https://scholar.google.com/citations?view_op=view_citation&hl=en&user=o9Gu_FMAAAAJ&pagesize=100&citation_for_view=o9Gu_FMAAAAJ:kNdYIx-mwKoC"
},
{
"title": "A Dedicated Mixed-Signal Characterisation and Testing Framework for Novel Digital Security Circuits That Use Carbon-Nanotube-Based Physical Unclonable Functions",
"authors": "F Frank, NA Anagnostopoulos, S Böttger, S Hermann, T Arul, ...",
"venue": "International Conference on Modern Circuits and Systems Technologies (MOCAST) 11, 2022",
"cites": 1,
"year": 2022,
"scholarUrl": "https://scholar.google.com/citations?view_op=view_citation&hl=en&user=o9Gu_FMAAAAJ&pagesize=100&citation_for_view=o9Gu_FMAAAAJ:2osOgNQ5qMEC"
},
{
"title": "On the sustainability of lightweight cryptography based on PUFs implemented on NAND Flash memories using programming disturbances",
"authors": "NA Anagnostopoulos, Y Fan, MU Saleem, N Mexis, F Frank, T Arul, ...",
"venue": "arXiv preprint arXiv:2204.02498, 2022",
"cites": 1,
"year": 2022,
"scholarUrl": "https://scholar.google.com/citations?view_op=view_citation&hl=en&user=o9Gu_FMAAAAJ&pagesize=100&citation_for_view=o9Gu_FMAAAAJ:u5HHmVD_uO8C"
},
{
"title": "Integrating physical unclonable functions from novel nanomaterials, circuit elements, and memory technologies into future hardware architectures",
"authors": "F Frank",
"venue": "Universität Passau, 2026",
"cites": 0,
"year": 2026,
"scholarUrl": "https://scholar.google.com/citations?view_op=view_citation&hl=en&user=o9Gu_FMAAAAJ&pagesize=100&citation_for_view=o9Gu_FMAAAAJ:ULOm3_A8WrAC"
},
{
"title": "Securing Next Generation Service-Oriented Vehicle Architectures",
"authors": "F Frank, D Püllen, C Krauss, D Spychalski",
"venue": "autotech. agil, 2024",
"cites": 0,
"year": 2024,
"scholarUrl": "https://scholar.google.com/citations?view_op=view_citation&hl=en&user=o9Gu_FMAAAAJ&pagesize=100&citation_for_view=o9Gu_FMAAAAJ:4TOpqqG69KYC"
},
{
"title": "Threat Analysis of the autotech. agil Cooperative Intelligent Transport System Ecosystem",
"authors": "F Frank, D Püllen, C Krauss, D Spychalski",
"venue": "autotech. agil, 2024",
"cites": 0,
"year": 2024,
"scholarUrl": "https://scholar.google.com/citations?view_op=view_citation&hl=en&user=o9Gu_FMAAAAJ&pagesize=100&citation_for_view=o9Gu_FMAAAAJ:_kc_bZDykSQC"
},
{
"title": "Safety & Security Co-Engineering in Autonomous Driving and Future Mobility Systems",
"authors": "N Braun, F Frank, C Krauss, D Spychalski, R Schubert, D Püllen",
"venue": "autotech. agil, 2024",
"cites": 0,
"year": 2024,
"scholarUrl": "https://scholar.google.com/citations?view_op=view_citation&hl=en&user=o9Gu_FMAAAAJ&pagesize=100&citation_for_view=o9Gu_FMAAAAJ:YOwf2qJgpHMC"
},
{
"title": "CNT-PUFs: Highly Robust and Heat-Tolerant Carbon-Nanotube-Based Physical Unclonable Functions for Stable Key Generation",
"authors": "S Böttger, F Frank, NA Anagnostopoulos, N Mexis, A Mohamed, ...",
"venue": "TechRxiv 2023 (0717), 2023",
"cites": 0,
"year": 2023,
"scholarUrl": "https://scholar.google.com/citations?view_op=view_citation&hl=en&user=o9Gu_FMAAAAJ&pagesize=100&citation_for_view=o9Gu_FMAAAAJ:W7OEmFMy1HYC"
}
]
}
+42 -2
View File
@@ -218,7 +218,7 @@ const en = {
},
team: {
title: ['Researchers,', 'engineers,', 'architects.'],
intro: 'The people who go deep on the problems that matter, from network software and private 5G to hardware and cloud-native systems.',
intro: 'The people who go deep on the problems that matter, from network, software to hardware and cloud-native systems. If it\'s a genuinely hard problem, we\'re in our element.',
photoAlt: 'The CipherCell team on a staircase in the offices in Passau.',
roles: {
ceo: 'Founder and CEO',
@@ -228,6 +228,26 @@ const en = {
studentF: 'Working student, development',
studentM: 'Working student, development',
},
research: {
title: ['Published', 'research.'],
intro: 'Papers by our team on Open RAN, cloud-native infrastructure and hardware security. The method we sell grew out of this work.',
sortLabel: 'Sort',
sort: { cites: 'Most cited', newest: 'Newest', oldest: 'Oldest' },
filterLabel: 'Category',
all: 'All',
showAll: (n: number) => `Show all ${n} papers`,
showFewer: 'Show fewer',
count: (shown: number, total: number) => `Showing ${shown} of ${total} papers`,
categories: {
'Open RAN & 6G': 'Open RAN & 6G',
'Hardware security': 'Hardware security',
'Automotive & mobility': 'Automotive & mobility',
'IoT security & privacy': 'IoT security & privacy',
'Wireless security': 'Wireless security',
'Cryptography': 'Cryptography',
'AI agents': 'AI agents',
} as Record<string, string>,
},
},
notFound: {
title: 'Page not found',
@@ -434,7 +454,7 @@ const de: typeof en = {
},
team: {
title: ['Forschende,', 'Ingenieure,', 'Architekten.'],
intro: 'Die Menschen, die sich in die Probleme vertiefen, auf die es ankommt: von Netzsoftware und privaten 5G-Netzen bis zu Hardware und Cloud-nativen Systemen.',
intro: 'Die Menschen, die sich in die Probleme vertiefen, auf die es ankommt: von Netzwerken über Software bis hin zu Hardware und Cloud-nativen Systemen. Wenn es sich um ein wirklich schwieriges Problem handelt, sind wir in unserem Element.',
photoAlt: 'Das CipherCell-Team auf einer Treppe in den Büros in Passau.',
roles: {
ceo: 'Gründer und CEO',
@@ -444,6 +464,26 @@ const de: typeof en = {
studentF: 'Werkstudentin, Entwicklung',
studentM: 'Werkstudent, Entwicklung',
},
research: {
title: ['Veröffentlichte', 'Forschung.'],
intro: 'Arbeiten unseres Teams zu Open RAN, cloud-nativer Infrastruktur und Hardware-Sicherheit. Die Methode, die wir anbieten, ist aus dieser Arbeit entstanden.',
sortLabel: 'Sortieren',
sort: { cites: 'Meistzitiert', newest: 'Neueste', oldest: 'Älteste' },
filterLabel: 'Kategorie',
all: 'Alle',
showAll: (n: number) => `Alle ${n} Arbeiten zeigen`,
showFewer: 'Weniger zeigen',
count: (shown: number, total: number) => `${shown} von ${total} Arbeiten`,
categories: {
'Open RAN & 6G': 'Open RAN & 6G',
'Hardware security': 'Hardware-Sicherheit',
'Automotive & mobility': 'Automotive & Mobilität',
'IoT security & privacy': 'IoT-Sicherheit & Datenschutz',
'Wireless security': 'Funksicherheit',
'Cryptography': 'Kryptografie',
'AI agents': 'KI-Agenten',
} as Record<string, string>,
},
},
notFound: {
title: 'Seite nicht gefunden',
+3
View File
@@ -3,6 +3,7 @@ import { Picture } from 'astro:assets';
import Base from '../layouts/Base.astro';
import teamPhoto from '../assets/images/team.png';
import { team } from '../data/team';
import Research from '../components/team/Research.astro';
import { t, type Lang } from '../i18n/ui';
interface Props { lang: Lang }
@@ -58,6 +59,8 @@ const ui = t(lang);
</ul>
</div>
</section>
<Research lang={lang} />
</Base>
<style>
+185
View File
@@ -0,0 +1,185 @@
/* Chladni figures for the research cards: the nodal lines of a vibrating plate, traced as the zero
contour of a wave field sampled on a grid (marching squares). Two fields:
plate z = a·sin(πn·x)·sin(πm·y) + b·sin(πm·x)·sin(πn·y), optionally rotated, which gives
rounded lattices, lenses and, turned, diamond and diagonal weaves;
sources waves from `sources` points on a circle around the centre, interfering:
z = Σ sin(2π·frequency·(phase − d / elasticity)), which gives rings, trefoils, petals.
Hovering a card tweens the numbers to its hover set, so the figure reflows into a related one
rather than sliding. Cheap by construction: a card draws once on load and on resize, and only
redraws during the 600 ms tween after a hover change. Nothing runs while the pointer is still. */
interface Common {
/** Centre of the sampled window. */
origin: [number, number];
/** Height of the sampled window; its width follows the canvas aspect. */
length: number;
/** Grid points per side. */
quality: number;
/** Mirror the lower-right quadrant into all four, around the canvas centre. */
quadrant?: boolean;
}
export interface PlateShape { a: number; b: number; n: number; m: number; rotation?: number }
export interface SourcesShape { frequency: number; phase: number; elasticity: number; radius: number; spin?: number }
export type ChladniParams =
| (Common & { mode: 'plate'; hover: Partial<PlateShape> } & PlateShape)
| (Common & { mode: 'sources'; sources: number; hover: Partial<SourcesShape> } & SourcesShape);
type Point = [number, number];
type Shape = Record<string, number>;
const DURATION = 600;
const PLATE_KEYS = ['a', 'b', 'n', 'm', 'rotation'] as const;
const SOURCE_KEYS = ['frequency', 'phase', 'elasticity', 'radius', 'spin'] as const;
/* cubic-bezier(0.2, 0, 0.3, 1): solved for x by Newton's method, then evaluated for y. */
function ease(t: number): number {
const x1 = 0.2, y1 = 0, x2 = 0.3, y2 = 1;
const bez = (u: number, p1: number, p2: number) => 3 * (1 - u) ** 2 * u * p1 + 3 * (1 - u) * u ** 2 * p2 + u ** 3;
const dbez = (u: number, p1: number, p2: number) => 3 * (1 - u) ** 2 * p1 + 6 * (1 - u) * u * (p2 - p1) + 3 * u ** 2 * (1 - p2);
let u = t;
for (let i = 0; i < 6; i++) {
const d = dbez(u, x1, x2);
if (Math.abs(d) < 1e-6) break;
u -= (bez(u, x1, x2) - t) / d;
}
return bez(Math.min(Math.max(u, 0), 1), y1, y2);
}
function fieldFor(p: ChladniParams, s: Shape): (x: number, y: number) => number {
if (p.mode === 'plate') {
const { a, b, n, m } = s;
const rot = s.rotation ?? 0;
const cos = Math.cos(rot), sin = Math.sin(rot);
const [ox, oy] = p.origin;
return (x, y) => {
// Rotate about the window centre, so turning the plate keeps the figure in frame.
const dx = x - ox, dy = y - oy;
const u = ox + dx * cos - dy * sin;
const v = oy + dx * sin + dy * cos;
return a * Math.sin(Math.PI * n * u) * Math.sin(Math.PI * m * v) + b * Math.sin(Math.PI * m * u) * Math.sin(Math.PI * n * v);
};
}
const pts: Point[] = Array.from({ length: p.sources }, (_, i) => {
const t = (i / p.sources) * 2 * Math.PI - Math.PI / 2 + (s.spin ?? 0);
return [p.origin[0] + s.radius * Math.cos(t), p.origin[1] + s.radius * Math.sin(t)];
});
const k = 2 * Math.PI * s.frequency;
const inv = 1 / s.elasticity;
return (x, y) => {
let z = 0;
for (const [sx, sy] of pts) z += Math.sin(k * (s.phase - inv * Math.hypot(x - sx, y - sy)));
return z;
};
}
function segments(p: ChladniParams, shape: Shape, w: number, h: number): Point[][] {
const q = p.quality;
const height = p.length;
const width = p.length * (w / h);
const x0 = p.origin[0] - width / 2;
const y0 = p.origin[1] - height / 2;
const sx = w / width;
const sy = h / height;
const start = p.quadrant ? Math.ceil(q / 2) - 1 : 0;
const f = fieldFor(p, shape);
const xs = new Float64Array(q);
const ys = new Float64Array(q);
for (let i = 0; i < q; i++) {
xs[i] = x0 + (i * width) / (q - 1);
ys[i] = y0 + (i * height) / (q - 1);
}
const z = new Float64Array(q * q);
for (let r = start; r < q; r++) for (let c = start; c < q; c++) z[r * q + c] = f(xs[c], ys[r]);
const lerp = (v0: number, v1: number, p0: number, p1: number) =>
Math.abs(v0 - v1) < 1e-10 ? (p0 + p1) / 2 : p0 + ((0 - v0) * (p1 - p0)) / (v1 - v0);
const out: Point[][] = [];
for (let r = start; r < q - 1; r++) {
for (let c = start; c < q - 1; c++) {
const tl = z[r * q + c], tr = z[r * q + c + 1], br = z[(r + 1) * q + c + 1], bl = z[(r + 1) * q + c];
const k = (tl > 0 ? 1 : 0) | (tr > 0 ? 2 : 0) | (br > 0 ? 4 : 0) | (bl > 0 ? 8 : 0);
if (k === 0 || k === 15) continue;
const xa = xs[c], xb = xs[c + 1], ya = ys[r], yb = ys[r + 1];
const top: Point = [(lerp(tl, tr, xa, xb) - x0) * sx, (ya - y0) * sy];
const right: Point = [(xb - x0) * sx, (lerp(tr, br, ya, yb) - y0) * sy];
const bottom: Point = [(lerp(bl, br, xa, xb) - x0) * sx, (yb - y0) * sy];
const left: Point = [(xa - x0) * sx, (lerp(tl, bl, ya, yb) - y0) * sy];
switch (k) {
case 1: case 14: out.push([left, top]); break;
case 2: case 13: out.push([top, right]); break;
case 3: case 12: out.push([left, right]); break;
case 4: case 11: out.push([right, bottom]); break;
case 6: case 9: out.push([top, bottom]); break;
case 7: case 8: out.push([left, bottom]); break;
case 5: out.push([left, top], [right, bottom]); break;
case 10: out.push([top, right], [left, bottom]); break;
}
}
}
return out;
}
export function mountChladni(canvas: HTMLCanvasElement, trigger: HTMLElement, params: ChladniParams, reduceMotion: boolean) {
const ctx = canvas.getContext('2d');
if (!ctx) return;
const keys: readonly string[] = params.mode === 'plate' ? PLATE_KEYS : SOURCE_KEYS;
const source = params as unknown as Shape;
// Every animatable key gets a number, 0 when the figure leaves it out (rotation, spin): a key
// that only the hover set names would otherwise tween from undefined and blank the figure.
const rest: Shape = {};
for (const k of keys) rest[k] = typeof source[k] === 'number' ? source[k] : 0;
const hover: Shape = { ...rest, ...(params.hover as Shape) };
let shape: Shape = { ...rest };
let frame = 0;
const draw = () => {
const dpr = window.devicePixelRatio || 1;
const w = Math.round(canvas.clientWidth * dpr);
const h = Math.round(canvas.clientHeight * dpr);
if (!w || !h) return;
if (canvas.width !== w || canvas.height !== h) { canvas.width = w; canvas.height = h; }
ctx.clearRect(0, 0, w, h);
ctx.strokeStyle = getComputedStyle(canvas).color;
ctx.lineWidth = dpr;
ctx.lineCap = 'round';
ctx.beginPath();
const segs = segments(params, shape, w, h);
const mirrors: Point[] = params.quadrant ? [[1, 1], [-1, 1], [1, -1], [-1, -1]] : [[1, 1]];
for (const [fx, fy] of mirrors) {
for (const [p0, p1] of segs) {
ctx.moveTo(fx === 1 ? p0[0] : w - p0[0], fy === 1 ? p0[1] : h - p0[1]);
ctx.lineTo(fx === 1 ? p1[0] : w - p1[0], fy === 1 ? p1[1] : h - p1[1]);
}
}
ctx.stroke();
};
const tweenTo = (target: Shape) => {
cancelAnimationFrame(frame);
if (reduceMotion) { shape = { ...target }; draw(); return; }
const from = { ...shape };
const t0 = performance.now();
const step = (now: number) => {
const t = Math.min((now - t0) / DURATION, 1);
const e = ease(t);
const next: Shape = {};
for (const k of Object.keys(target)) next[k] = from[k] + (target[k] - from[k]) * e;
shape = next;
draw();
if (t < 1) frame = requestAnimationFrame(step);
};
frame = requestAnimationFrame(step);
};
trigger.addEventListener('pointerenter', () => tweenTo(hover));
trigger.addEventListener('pointerleave', () => tweenTo(rest));
trigger.addEventListener('focus', () => tweenTo(hover));
trigger.addEventListener('blur', () => tweenTo(rest));
new ResizeObserver(draw).observe(canvas);
draw();
}
+20
View File
@@ -10,6 +10,26 @@
--night: #0E100F;
--line: rgb(26 26 24 / 0.14);
/* Logo palette: only for the abstract fields (hero, research cards), never for text or UI. */
--logo-deep: #284888;
--logo-mid: #386898;
--logo-teal: #48A8B8;
--logo-cyan: #58C8D8;
/* Neighbours of the logo blues for the research card fields, so the cards vary without leaving
the brand: same coolness and depth, different hue. Fields only, never text or UI. */
--field-indigo: #3B4096;
--field-petrol: #1E5A63;
--field-sea: #2E7C72;
--field-steel: #4D6882;
--field-dusk: #55508F;
--field-cobalt: #2B56B0;
--field-ocean: #1F4E86;
--field-glacier: #7FB4CF;
--field-aqua: #3EAFB2;
--field-jade: #3B8C78;
--field-slate: #3A4862;
--field-plum: #66508E;
/* Immutable copies for resetting inside a .night or overlay context */
--ink-base: #1A1A18;
--ink-muted-base: #57564F;