designpaca/apps/site/public/work/overtone-sound/index.html
Yun Chan e09d4efb1c
Some checks failed
ci / build (push) Has been cancelled
feat: strengthen design skill and refresh all showcases
2026-09-12 18:16:33 +09:00

239 lines
9.6 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!doctype html>
<html lang="ko">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>오버톤 사운드랩 · 공간 음향 연구소</title>
<meta
name="description"
content="공간 음향 작업을 기록하는 방식을 보여 주는 가상 아카이브입니다. 작업·장소·수치는 예시입니다."
/>
<meta property="og:title" content="오버톤 사운드랩" />
<meta
property="og:description"
content="공간 음향 작업을 읽는 가상 아카이브 구성."
/>
<meta property="og:type" content="website" />
<meta name="theme-color" content="#12100e" />
<link rel="preconnect" href="https://cdn.jsdelivr.net" crossorigin />
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/gh/orioncactus/pretendard@v1.3.9/dist/web/variable/pretendardvariable-dynamic-subset.min.css"
/>
<link rel="stylesheet" href="./styles/main.css?v=20260912all" />
<link
rel="icon"
href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'%3E%3Crect width='32' height='32' fill='%230E0D0B'/%3E%3Cpath d='M4 16h4l2-7 3 14 3-10 2 5h10' fill='none' stroke='%23E8A33D' stroke-width='1.8'/%3E%3C/svg%3E"
/>
</head>
<body>
<a class="skip" href="#main">본문으로 건너뛰기</a>
<header class="site-head">
<div class="wrap head-inner">
<a class="wordmark" href="#main">오버톤 사운드랩</a>
<nav class="nav" aria-label="섹션">
<a href="#works">작업</a>
<a href="#manifesto">선언</a>
<a href="#contact">연락</a>
</nav>
</div>
</header>
<main id="main">
<section class="hero" aria-labelledby="hero-h">
<div class="wrap hero-inner">
<div class="hero-copy">
<p class="hero-kicker reveal">
<span>가상 공간 음향 아카이브</span>
<span class="mono">작업 기록 형식 예시</span>
</p>
<h1 id="hero-h" class="reveal" style="--delay: 80ms">
침묵과 폭발의<br /><em>주파수</em>
</h1>
<p class="hero-statement reveal" style="--delay: 160ms">
오버톤은 소리가 공간에서 어떻게 달라지는지를 기록하는 방식을 보여 줍니다.
곡선과 아래 작업·장소·수치는 화면 구성을 위한 예시이며 실제 작업이나
협업 이력을 뜻하지 않습니다.
</p>
</div>
<div class="wave-surface">
<canvas id="wave" class="wave" aria-hidden="true"></canvas>
</div>
</div>
</section>
<section id="works" class="section works" aria-labelledby="works-h">
<div class="wrap">
<h2 id="works-h" class="works-h reveal">작업</h2>
<p class="muted reveal" style="--delay: 30ms">
아래 제목, 연도, 공간, 기술 조건은 음향 작업 아카이브의 표현 방식을 설명하는 예시입니다.
</p>
<ol class="archive">
<li class="work reveal">
<p class="work-year mono">2026</p>
<h3>공명하는 방 — 전시장 설치 예시</h3>
<p>
종이 콘 스피커가 공간의 반사를 되돌려주는 설치 구성 예시입니다.
소리의 지연과 관람객 동선을 함께 기록하는 방식을 보여 줍니다.
</p>
<p class="work-meta mono">48ch · 공간지연 · 상설</p>
</li>
<li class="work work-b reveal" style="--delay: 60ms">
<p class="work-year mono">2025</p>
<h3>무의 스펙트럼 — 공연 구성 예시</h3>
<p>
조용한 공간의 바닥 소음을 다루는 공연 구성 예시입니다. 표기된 수치는
실제 측정값이나 공연 기록이 아닙니다.
</p>
<p class="work-meta mono">live · −62dB floor</p>
</li>
<li class="work reveal" style="--delay: 40ms">
<p class="work-year mono">2024</p>
<h3>파도의 인버전 — 해안 설치 예시</h3>
<p>
바람과 파도라는 소재를 반대 위상으로 해석하는 해안 설치 구성 예시입니다.
</p>
<p class="work-meta mono">field data · 6h loop</p>
</li>
<li class="work work-b reveal" style="--delay: 100ms">
<p class="work-year mono">2023</p>
<h3>유리 진동자 — 연주 설비 예시</h3>
<p>
유리잔의 높이와 공진을 조율하는 연주 설비 구성 예시입니다.
</p>
<p class="work-meta mono">glass · 220–940Hz</p>
</li>
</ol>
</div>
</section>
<section id="manifesto" class="section manifesto" aria-labelledby="manifesto-h">
<div class="wrap">
<h2 id="manifesto-h" class="sr-only">선언</h2>
<p class="manifesto-line reveal">
좋은 음향은 들리지 않는다.<br />
방이 좋아지는 것뿐이다.
</p>
<p class="manifesto-note reveal" style="--delay: 100ms">
이 구성은 음파의 크기보다 공간의 반사와 잔향을 먼저 읽게 합니다.
시각화가 소리의 모든 경험을 대신할 수 없다는 점을, 작업 기록의 순서로
보여 줍니다.
</p>
</div>
</section>
<div class="marquee" role="group" aria-label="협업 장소 유형 예시">
<div class="marquee-track">
<span>공공 전시장</span><span>도심 복합문화공간</span><span>콘서트홀</span><span>소극장</span><span>해안 야외 공연장</span><span>공공 전시장</span><span>도심 복합문화공간</span><span>콘서트홀</span><span>소극장</span><span>해안 야외 공연장</span>
</div>
</div>
<section class="section photo" aria-label="설치 현장">
<figure class="wrap photo-fig reveal">
<img
src="./assets/installation.webp"
alt="어두운 전시실에 종이콘 스피커들이 늘어서 있고 앰버색 빛줄기가 한 줄 지나간다"
width="1440"
height="810"
loading="lazy"
/>
<figcaption class="mono">공명하는 방 · 설치 연출 이미지</figcaption>
</figure>
</section>
<section id="contact" class="section contact" aria-labelledby="contact-h">
<div class="wrap contact-grid">
<h2 id="contact-h">구성 안내</h2>
<p>
이 페이지는 공간 음향 작업을 설명하는 구성 예시입니다. 실제 설치·공연·연구
협업이나 연락을 접수하지 않습니다.
</p>
<a class="contact-mail" href="#main">작업 구성 다시 보기</a>
</div>
</section>
</main>
<footer class="site-foot">
<div class="wrap foot-inner">
<p>오버톤 사운드랩 · 가상 공간 음향 아카이브</p>
<p class="mono foot-meta">실제 협업 단체·장소·연락처를 제공하지 않습니다.</p>
</div>
</footer>
<script>
const canvas = document.getElementById("wave");
const reduced = matchMedia("(prefers-reduced-motion: reduce)").matches;
const ctx = canvas.getContext("2d");
let w, h, dpr, running = false;
function fit() {
dpr = Math.min(devicePixelRatio || 1, 1.5);
w = canvas.clientWidth;
h = canvas.clientHeight;
canvas.width = Math.round(w * dpr);
canvas.height = Math.round(h * dpr);
ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
}
function draw(t) {
ctx.clearRect(0, 0, w, h);
for (let layer = 0; layer < 3; layer++) {
ctx.beginPath();
const amp = (h * 0.16) * (1 - layer * 0.28);
const yMid = h * (0.52 + layer * 0.12);
const speed = 0.00042 * (1 + layer * 0.7);
for (let x = 0; x <= w; x += 3) {
const p = x / w;
const env = Math.exp(-Math.pow((p - 0.5) * 2.4, 2));
const y =
yMid +
Math.sin(p * 21 + t * speed + layer * 2.1) * amp * env * Math.sin(p * Math.PI) +
Math.sin(p * 53 - t * speed * 1.7) * amp * 0.25 * env;
x === 0 ? ctx.moveTo(x, y) : ctx.lineTo(x, y);
}
ctx.strokeStyle = layer === 0 ? "rgba(232,163,61,0.85)" : `rgba(232,163,61,${0.32 - layer * 0.1})`;
ctx.lineWidth = layer === 0 ? 1.4 : 1;
ctx.stroke();
}
}
function loop(t) {
if (!running) return;
draw(t);
requestAnimationFrame(loop);
}
fit();
addEventListener("resize", fit, { passive: true });
draw(0);
if (!reduced) {
const io = new IntersectionObserver((entries) => {
const on = entries[0].isIntersecting;
if (on && !running) {
running = true;
requestAnimationFrame(loop);
} else if (!on) {
running = false;
}
});
io.observe(canvas);
}
const io2 = new IntersectionObserver(
(entries) => {
for (const e of entries) {
if (!e.isIntersecting) continue;
e.target.classList.add("is-in");
io2.unobserve(e.target);
}
},
{ rootMargin: "0px 0px -8% 0px", threshold: 0.05 },
);
for (const el of document.querySelectorAll(".reveal")) io2.observe(el);
</script>
</body>
</html>