
「ページ」から「体験」へ ── 没入型パーソナルHPのアーキテクチャ解剖
このサイトを開いた瞬間のことを想像してほしい。
暗い画面に、ノイズが走る。文字が炸裂するように現れ、背景のパーティクルが静かに呼吸している。そしてスクロールを始めると、映像が徐々にズームし、瞳の奥へと吸い込まれていく ── 白い光が世界を飲み込み、哲学の言葉が折り重なりながら特異点へと収束する。
これは「Webページ」ではない。スクロールで駆動する、インタラクティブな映像体験だ。
前回の記事ではAIを活用してこのサイトを1日で構築したプロセスを、その次の記事ではAI生成UIの均質化から抜け出すための理論と実践を解説した。今回は、その先にあるもの ── 実際に構築された没入型体験の技術アーキテクチャを、コードレベルで解剖する。
映像編集やアニメのOP映像で用いられるハードカット、フラッシュカット、マッチカット。これらの映像文法を、スクロールというインタラクションに移植することで、Webページを「見るもの」から「体験するもの」へと昇華させた。その仕組みを、基盤となるスムーススクロールから3Dパーティクル、そして映像カットの実装まで、一つずつ紐解いていく。
1. Lenis × GSAP ── 全てを繋ぐ「時間」の基盤
このサイトの全ての演出は、スクロール位置をタイムラインとして機能させることで成り立っている。その基盤となるのが、Lenis(スムーススクロールライブラリ)とGSAP ScrollTriggerの完全同期だ。
なぜ同期が不可欠なのか
Lenisはブラウザ標準のスクロール挙動をオーバーライドし、独自の補間(lerp)による滑らかなスクロールを提供する。GSAP ScrollTriggerは、スクロール位置に連動してアニメーションを制御する。この2つが独立して動くと、滑らかなスクロールに対してアニメーションが遅れて追従するという致命的なズレが発生する。
SmoothScroll.tsx では、この問題をGSAPのticker(内部的な時計)でLenisを駆動することで解決している:
// GSAP ticker で Lenis を駆動(rAF の代わり)
const tickerCallback = (time: number) => {
instance.raf(time * 1000);
};
gsap.ticker.add(tickerCallback);
gsap.ticker.lagSmoothing(0);
Lenisのraf()をGSAPのtickerコールバック内で呼び出すことで、GSAPが唯一のクロックソースとなる。さらに、Lenisのscrollイベント内でScrollTrigger.update()を呼び出し、スクロール位置の変更を即座にGSAPに伝播。lagSmoothing(0)でGSAPのラグ補正を無効化し、Lenis独自の補間と競合しないようにしている。
このコンポーネントはReact Contextを通じて、Lenisインスタンス(useLenis())と正規化されたスクロール進捗0〜1(useScrollProgress())をサイト全体に提供する。Heroのシネマティックタイムラインも、3Dパーティクルのモーフィングも、全てはこの一つの「時間」に同期している。
2. シネマティックスクロールエンジン ── 200vhの映画的演出
Heroセクションは200vhの高さを持ち、内部のコンテナはsticky top-0で画面に固定される。つまり、ユーザーが100vh分スクロールする間、画面上のコンテンツは固定されたままGSAP ScrollTriggerのタイムラインが進行する ── スクロール位置が、映画の再生ヘッドそのものになる。
3フェーズの映画的遷移
タイムラインは3つのフェーズで構成されている:
Phase 1(0〜30%): 装飾のフェードアウト
背景の巨大なテキストがscale: 3 → 1で収縮し、blob装飾が上へ漂い、3Dパーティクルが透明に溶ける。ヒーローコンテンツは25%まで表示された後、y: -80で上方にフェードアウト。
Phase 2(30〜40%): 瞳フレームへのフリーズ
背景動画がcurrentTime = 0.3の「瞳」のフレームで一時停止。オーバーレイヴェールが溶解し、グレースケール/コントラストのフィルターが自然な色調へ遷移する。この一時停止はGSAPタイムラインのコールバックで瞬時に実行され、スクロールの遅延(scrub lag)の影響を受けない。
Phase 3(40〜100%): 瞳へのズームとホワイトフラッシュ
動画がscale: 15で瞳の中へズームする。transformOriginはビューポート幅ごとに校正されたgetPupilOrigin()関数で決定される(1920px以上なら47% 42%、1440px以上なら48% 44%...)。同時に、fixed配置の全画面ホワイトフラッシュ(z-index: 101)がsine.inイージングで徐々に白へ被っていく。これは映像編集におけるフェードトゥホワイトに相当する。
重要な設計判断:このシネマティックフラッシュはstickyではなくfixedで配置されている。これにより、Heroセクションのoverflow: hiddenにクリップされることなく、次のPhilosophyセクションまでシームレスに橋渡しする。
また、動画の再生速度はメインタイムラインとは独立したScrollTriggerで制御されている。スクロール開始から30%の間にplaybackRateが1xから5xへ直線的に加速する。これをタイムラインに含めず分離することで、スクロールのラグが動画の速度制御に影響しないようにしている。
3. 特異点コラプス ── 500vhの時間的収束
Philosophyセクションは500vhの高さを持ち、margin-top: -200vhでHeroセクションと重なる。内部コンテナはCSS stickyで画面に固定され、400vh分のスクロール駆動アニメーションが展開される。
メッセージの蓄積と劣化
哲学的なメッセージが一つずつ登場し、GSAPのfromToでscale: 1.3 → 1, opacity: 0 → 1と着地する。新しいメッセージが現れるたびに、以前のメッセージはopacity: 0.15, scale: 0.98へと劣化し、層として蓄積していく。
メッセージの配置は極端だ。clamp(4rem, 13vw, 15rem)のフォントサイズ、top: -8%, left: -15%のような画面外への逸脱、mix-blend-mode: exclusionやdifferenceによる色彩の混合、そしてWebkitTextStrokeによるアウトライン文字。これらは通常のUI設計では禁忌とされる手法だが、ここでは映像のレイヤー合成として機能している。
DenseTextNoise ── 52のジェネレーティブノイズ要素
DenseTextNoiseコンポーネントは、4つのムーブメントグループで構成されるジェネレーティブなテキストノイズシステムだ:
- Group A: 40の静的散乱テキスト(14〜80px、2〜7%の不透明度、
screenブレンド) - Group B: 5つの水平フローストリップ(左右交互の方向)
- Group C: 4つの垂直フローストリップ(
writing-mode: vertical-rl) - Group D: 3つの対角ストリップ
全ての配置はシード付き決定論的乱数(Lehmer/Park-Miller:s * 16807 % 2147483647)で生成される。これにより、完全にランダムに見えながらも、サーバー側レンダリングとクライアント側で一貫した配置が保証される。
加えて、CodeRainキャンバスがマトリクス風の文字落下効果を描画する。実際のコードスニペットを文字として使用し、ゴールドのヘッドに緑のトレイルが続く。
シンギュラリティ・コラプス
スクロールの終端で、全てが一つの点へ収束する。200vmaxの円形マスクコンテナがscale: 0.002へ縮小し、内部のコンテンツは逆にスケールしながら90度回転する。ブレンドモードは全てnormalに切り替えられ、合成のちらつきを防ぐ。そしてコンテナがautoAlpha: 0で消失し、背後に控えていたAboutセクションが姿を現す。
この収束は、物理学におけるブラックホールの特異点 ── 無限の密度が一点に凝縮する状態 ── を視覚的に表現したものだ。500vhもの長いスクロールを通じて蓄積された情報とノイズが、一瞬で消失する。その喪失感と解放感が、次のセクションへの移行を感情的に演出する。
4. 3Dパーティクルモーフィング ── スクロールで変容するデジタル物質
Heroセクションの背景では、300個のパーティクルがスクロール進捗に応じて3つの相(フェーズ)の間をモーフィングする。
MorphingParticleField.tsxは、3つのFloat32Arrayに格納された位置セットをスクロール進捗でブレンドする:
- Phase 1(0〜15%): 構造化グリッド ── 正方グリッドにsin波のz変位を加えた秩序だった配置
- Phase 2(30〜45%): ブリザード ── 12×12×12の立方体内のランダムな位置
- Phase 3(60%+): ネットワーク球体 ── 球面上に配置され、一定距離内のノード間に接続線を描画
位置のブレンドは1フレームあたり3%のlerp補間で行われる。つまり、目標位置への到達は瞬時ではなく、有機的で粘り気のある遷移となる。
ネットワークフェーズでは、パーティクル間の距離をO(n²)で計算し、距離2.0以内のペアに最大500本の接続線を描画する。色はサイトのアクセントカラーであるゴールド(#C59D5F)で、距離に応じたアルファフェードが適用される。パーティクルもラインもTHREE.AdditiveBlendingで描画され、暗い背景に対して発光的なエフェクトを生み出す。
この3Dシーンのスクロール連動は、useScrollProgress()コンテキストから0〜1の値を読み取ることで実現されている。Lenis → GSAP ticker → ScrollTrigger → React Context → R3F useFrame という、サイト全体の時間軸が一貫してパーティクルに伝播している。
なお、このコンポーネントはモバイルではnullを返す。後述するが、モバイルでは「別の体験」を提供するという設計判断だ。
5. 映像編集文法のスクロール体験への転用
ここまでの解説で読者は気づいているかもしれない。このサイトは映像編集の文法で構成されていると。
映画やアニメのOP映像では、シーン間の遷移にハードカット、フラッシュカット、マッチカットといった技法が使われる。このサイトでは、これらをSectionTransition.tsxでスクロール遷移に直接移植した。
3種の映像カットの実装
IntersectionObserverがdata-transition属性付きのセクションを監視し、ビューポートに入ると対応するカットを発火する:
ハードカット(hard-cut) 明るい水平ラインが上から下へ掃引するスキャンライン・ワイプ(0.35s)。直後にブラックオーバーレイが60msでフラッシュインし、150msでフェードアウト。映像編集の「カットアウェイ」に相当する、瞬時の視覚的断絶だ。
カラーシフト(color-shift) 薄いホワイトのブライトネスフラッシュ(0.08sイン→0.4sアウト)。映像のカラーグレーディングにおけるウォームシフトに相当し、セクション間の「ムード転換」を暗示する。劇的な変化ではなく、空気感が変わる程度の微細なシフトだ。
マッチカット(match-cut)
ゴールド(#C59D5F)の垂直ラインが画面左端から右端へスラッシュする(0.3s)。映像編集におけるマッチカット ── 類似するビジュアル要素でシーンを繋ぐ技法 ── を、セクション間の視覚的連続性として再解釈している。
全てのカットには600msのクールダウンが設定され、連続発火を防止する。これは映像における「カットの間合い」── 視聴者がカットを受け止めるための最小限の時間 ── を再現したものだ。
カット配置戦略
page.tsxでは、各セクション間の遷移に意図的なカットタイプを割り当てている:
| 遷移 | カットタイプ | 意図 |
|---|---|---|
| Hero → Philosophy | マッチカット | 映像的連続性 ── 瞳ズームから哲学の世界観へ |
| About → Services | カラーシフト | ムード転換 ── 自己紹介から提供価値へ |
| Skills → Works | ハードカット | 技術スタックから実績への力強い転換 |
| LifeLog → Contact | ハードカット | 日常からアクションへの最終カット |
さらに、セクションの合間にはRunningMarqueeが配置される。BUILD → SHIP → ITERATE、CRAFT · CODE · CREATE · SHIP · REPEAT といったテキストが、5色のバーストパレット(ゴールド、エレクトリックブルー、ホットピンク、ライム、バイオレット)で水平に流れる。これは映像におけるタイトルカードやインタータイトルに相当し、シーンの区切りを視覚的に強調すると同時に、次のセクションのエネルギーを予兆させる。
6. デュアルエンジン・アニメーション ── GSAPとFramer Motionの棲み分け
このサイトは2つのアニメーションエンジンを明確に棲み分けして使用している:
GSAP ScrollTrigger ── スクロール駆動アニメーション。シネマティックタイムライン、SVGドローオン、セクション遷移、パーティクルフェード。スクロール位置をタイムラインに直接マッピングする必要がある場面で使用。主にデスクトップ限定。
Framer Motion ── 登場アニメーション、インタラクティブ状態、ホバーエフェクト。スクロールとは独立して動作する要素に使用。デスクトップ・モバイル両対応。
animations.tsには12のFramer Motionバリアントが定義されており、全てが統一イージング[0.25, 0.1, 0.25, 1]を共有している。fadeInUp、scaleIn、clipRevealUp、springBounceなどが、セクション全体で一貫したアニメーションの語彙を形成する。staggerContainerは子要素を0.1秒間隔で順次表示し、映像の「ディゾルブ」に似たカスケード効果を生み出す。
この2エンジンの共存を可能にしているのが、前述のLenis × GSAP同期基盤だ。Framer Motionは独自のuseInViewでviewport進入を検知し、GSAPはScrollTriggerでスクロール位置に連動する。それぞれが独立したトリガーメカニズムを持つため、競合することなく同じページ上で機能する。
7. インタラクティブ要素の微細工学
映像的なマクロ構造に加えて、このサイトはマイクロインタラクションにも工学的なこだわりがある。
CustomCursor
デスクトップ環境では、標準のカーソルがデュアル要素のカスタムカーソルに置き換えられる。8pxのゴールドドットがuseMotionValueで即座に追従し、40pxのリングがuseSpring(stiffness: 500, damping: 28, mass: 0.5)のスプリング物理で遅れて追従する。インタラクティブ要素にホバーするとリングが56pxへ拡大する。
GlowCard
3Dパースペクティブティルト(±8度)とマウス追従グレアを備えたカードコンポーネント。requestAnimationFrameでmousemoveをデバウンスし、ラジアルグラデーションのオーバーレイがカーソル位置を追跡する。グラスモーフィズム(backdrop-blur-md)のベースに、ホバーでゴールドのボーダーグローが点灯する。
SVGDivider
6つのSVGパスバリアント(wave、geometric、dots、circuit、spiral、brush)が、スクロールに連動してパスを描画する。デスクトップではGSAP ScrollTriggerのstrokeDashoffsetアニメーション、モバイルではFramer MotionのpathLengthアニメーションで、プラットフォームに応じた最適な手法が選択される。
ドットベース・ナビゲーション
Navigationは、従来のテキストメニューを2〜3pxのドットに置き換えている。ラベルはホバー時のみスプリングアニメーションで展開され、アクティブセクションのドットは1 → 1.8 → 1のスケールパルスで呼吸する。ページ遷移にはView Transitions API(document.startViewTransition())を使用し、ブラウザネイティブのクロスフェードが適用される。
これらは全て「意図的な摩擦」だ。カーソルは透明なツールではなく、ナビゲーションは即座に視認可能ではなく、カードは平面に留まらない。ユーザーに探索を強いることで、能動的な関与を引き出す設計になっている。
8. デザインシステムとポストプロセッシング
Warm Dark パレット
背景色は純粋な黒ではなく#171311(わずかに暖色がかったダークブラウン)。アクセントのゴールド#C59D5Fに、エレクトリックブルー#00D4FF、ホットピンク#FF2D7B、ライム#BFFF00、バイオレット#A855F7のバースト4色を組み合わせている。
この配色は映像のカラーグレーディングに近い。基調は暗く暖かいが、要所で鋭い色が「刺さる」ことで、均質な暗さの中に視覚的なリズムが生まれる。
4フォント戦略
4つのGoogleフォントをnext/fontで最適化ロードし、CSS変数で参照する:
- Space Grotesk(見出し)── 幾何学的で現代的
- Inter(本文)── 最高の可読性
- Fira Code(コード・ターミナル)── 等幅の技術的質感
- Noto Sans JP(日本語)── 和文の自然な統合
ポストプロセッシングレイヤー
サイトの雰囲気を決定づけるのが、fixed配置のポストプロセッシングレイヤー群だ:
- GrainOverlay(z-100)── SVG
feTurbulenceのフラクタルノイズ。5%の不透明度でフィルムグレインを再現 - CRTScanlines(z-99)── 2px間隔の走査線に、CRTフリッカーアニメーション、RGB色収差、ビネット
- SectionTransition(z-98)── 前述の映像カットシステム
レイヤーの順序は意図的だ。グレインはスキャンラインの上、スキャンラインはセクション遷移の上に配置される。つまり、セクション遷移のフラッシュでさえ、フィルムグレインのテクスチャが上から被さる。この積み重ねが、サイト全体を「一つの映像フィルム」のように見せる。
9. モバイル戦略 ── 優雅な変容
このサイトのモバイル対応は、「デスクトップ体験の縮小版」ではない。全く別の体験として設計されている。
useIsDesktop()フックが分岐の中心で、以下のコンポーネントで使用される:
- MorphingParticleField: モバイルでは
nullを返し、CSSグラデーションにフォールバック - CustomCursor: タッチデバイスでは不要なため
null - CRTScanlines: パフォーマンスと画面サイズの観点から
null - SectionTransition: タッチスクロールの挙動が異なるため
null - SVGDivider: GSAPのスクロールドローイングからFramer Motionの
pathLengthアニメーションに切り替え - PhilosophySection: フォントサイズを55%にスケーリング
つまり、モバイルでは3DパーティクルもカスタムカーソルもCRT効果も映像カットも存在しない。しかし、コンテンツの可読性、スムーススクロール、Framer Motionの登場アニメーションは維持される。
これは意図的な設計判断だ。タッチデバイスではホバーという中間的なインタラクション層が存在せず、画面も小さく、通信環境も不安定になり得る。重い演出を無理に適用するよりも、プラットフォームの特性に合わせた「別の正解」を提供する方が、結果的にユーザー体験が向上する。
おわりに ── スクロールは新しいフィルムである
このサイトのアーキテクチャを一言で表すなら、**「スクロール位置=タイムライン、ビューポート=フレーム、ユーザー=監督」**だ。
200vhのHeroセクションは映画のオープニングショット、500vhのPhilosophyセクションは中盤のクライマックス、3種の映像カットはシーン間のトランジション、RunningMarqueeはインタータイトル。そして最後のContactセクションは、エンドクレジットに続く「次回予告」のような存在だ。
映像編集の文法をWebに移植することは、単なる見た目の問題ではない。ユーザーのスクロールという能動的な行為が、映像の「再生」そのものになる。そしてスクロールの速度は、映画の再生速度に相当する。早送りすれば情報は流れ、ゆっくり見ればディテールが浮かび上がる。
UI/UXのセオリーを意図的に逸脱することで、このサイトは「ページ」から「体験」へと変容した。グリッドの破壊、非線形のナビゲーション、意図的な摩擦、ジェネレーティブなノイズ ── それらは全て、計算されたカオスとして機能している。ルールを破る前にルールを理解し、破壊する前に理由を持つ。その積み重ねが、記憶に残る体験を生み出す。
このサイトはAIとの協働によって構築された。しかし、AIが出力したのはコードであり、体験の設計ではない。シネマティックなタイムラインの構想、映像カットの配置戦略、500vhの特異点コラプスというコンセプト ── それらの「なぜ」は人間が決定し、AIが「どのように」を実装した。AIでサイトを構築したプロセスと、AI生成UIの均質化からの脱却手法については、それぞれの記事で詳しく解説している。
Webのフロンテイアは「より多くの機能」や「より多くのピクセル」ではない。ブラウザをドキュメントビューアーから、時間的・空間的・感情的な表現媒体へと変えることだ。このサイトは、その小さな実験である。