239 lines
9.6 KiB
HTML
239 lines
9.6 KiB
HTML
<!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>
|