[{"data":1,"prerenderedAt":567},["ShallowReactive",2],{"detailshader-basics":3,"$fDsncH6IM7nVg3co3wSMxMRFxpkmiG1MfgExjIOLyRmY":163,"$fS4sOnCUpoF-wMfw16HWwnBnngwddN9OaX5p2KHuFyVA":190,"works":403},{"id":4,"createdAt":5,"updatedAt":6,"publishedAt":7,"revisedAt":6,"title":8,"content2":9,"eyecatch":10,"tag":14,"heading_list":36,"recruit":20,"is_html":20,"description":37,"related_articles":38,"recommended_articles":162,"is_migration":20},"shader-basics","2026-06-16T01:35:56.924Z","2026-07-09T04:55:35.213Z","2026-07-07T10:13:33.054Z","WebGLシェーダーの基本とテクスチャー表現","\u003Cp>月末にセールをするのはやめてほしいです。\u003C/p>\u003Cp>この記事は、社内イベント「\u003Ca href=\"https://www.s2factory.co.jp/blog/tea/\" target=\"_blank\" rel=\"noopener noreferrer\">お茶会\u003C/a>」での発表内容をもとにまとめたものです。\u003Cbr>今回は、弊社の morisaki が\u003Cstrong>「シェーダー」\u003C/strong>について発表しました。\u003C/p>\u003Chr>\u003Cp>\u003Cbr>フロントエンドの実装で使われる \u003Ca href=\"https://developer.mozilla.org/ja/docs/Web/API/WebGL_API\" target=\"_blank\" rel=\"noopener noreferrer\">\u003Cstrong>WebGL\u003C/strong>\u003C/a> や \u003Ca href=\"https://threejs.org/\" target=\"_blank\" rel=\"noopener noreferrer\">\u003Cstrong>Three.js\u003C/strong>\u003C/a> を例に、シェーダーがどのように\u003Cstrong>画面上の表現\u003C/strong>を作っているのか、基本的な仕組みから実演を交えて紹介したいと思います。\u003C/p>\u003Ch2 id=\"h4db06e3d9d\">シェーダーとは\u003C/h2>\u003Cp>シェーダーは、\u003Ca href=\"https://ja.wikipedia.org/wiki/GPU\" target=\"_blank\" rel=\"noopener noreferrer\">\u003Cstrong>GPU\u003C/strong>\u003C/a>\u003Cstrong> 上で動作する描画用のプログラム\u003C/strong>です。\u003C/p>\u003Cp>フロントエンドの実装で使う場合は、WebGL 上で動かすことが多く、\u003Ca href=\"https://developer.mozilla.org/ja/docs/Games/Techniques/3D_on_the_web/GLSL_Shaders\" target=\"_blank\" rel=\"noopener noreferrer\">\u003Cstrong>GLSL(OpenGL Shading Language)\u003C/strong>\u003C/a>\u003Cstrong>という言語で記述\u003C/strong>します。ほかにも、DirectX で使われる \u003Ca href=\"https://learn.microsoft.com/ja-jp/windows/win32/direct3dhlsl/dx-graphics-hlsl\" target=\"_blank\" rel=\"noopener noreferrer\">HLSL\u003C/a> や、\u003Ca href=\"https://developer.mozilla.org/ja/docs/Web/API/WebGPU_API\" target=\"_blank\" rel=\"noopener noreferrer\">WebGPU\u003C/a> で採用されている \u003Ca href=\"https://www.w3.org/TR/WGSL/\" target=\"_blank\" rel=\"noopener noreferrer\">WGSL\u003C/a> など、用途に応じたシェーディング言語があります。\u003C/p>\u003Cp>通常、コンピューターの処理は CPU が担当します。\u003Cbr>\u003Cstrong>CPU はひとつひとつの計算は得意\u003C/strong>ですが、大量の処理を同時に行うことにはあまり向いていません。\u003C/p>\u003Cp>一方で \u003Cstrong>GPU\u003C/strong> は、\u003Cstrong>たくさんの計算を並列で処理\u003C/strong>することに向いており、何千、何万というパーティクルを動かしたり、画面全体に複雑な表現を加えたりするような場合に力を発揮します。\u003C/p>\u003Cp>WebGLで使うシェーダーは、主に 2 種類です。\u003C/p>\u003Cul>\u003Cli>\u003Cstrong>Vertex Shader\u003C/strong>：ジオメトリの各頂点の位置を計算する。\u003C/li>\u003Cli>\u003Cstrong>Fragment Shader\u003C/strong>：各フラグメントの色を計算する。\u003C/li>\u003C/ul>\u003Cp>処理は、\u003Cstrong>Vertex Shader → Fragment Shader\u003C/strong> の順に実行されます。\u003Cbr>Vertex Shader\u003Cstrong> \u003C/strong>で計算した値を Fragment Shader へ渡すことはできますが、\u003Cstrong>逆方向には渡せません\u003C/strong>。\u003C/p>\u003Cp>シェーダーを使うことで、Three.js の組み込みマテリアルだけでは難しい質感や動き、細かなエフェクトも制御しやすくなります。\u003Cstrong>もともとは 3D オブジェクトの影や陰影を計算するためのもの\u003C/strong>でしたが、現在では Web 上のさまざまな表現に使われています。\u003C/p>\u003Ch3 id=\"hdc136c4370\">シェーダーで扱う値と関数\u003C/h3>\u003Cp>シェーダーでは、頂点の位置や色、時間、テクスチャーなどの値を受け取り、計算した結果を描画に反映していきます。\u003C/p>\u003Ch4 id=\"h16bf73988b\">\u003Cstrong>変数の種類(修飾子)\u003C/strong>\u003C/h4>\u003Cp>値の渡し方は、修飾子で指定します。代表的なものが、\u003Ccode>attribute\u003C/code>、\u003Ccode>uniform\u003C/code>、\u003Ccode>varying\u003C/code> です。\u003C/p>\u003Cul>\u003Cli>\u003Ccode>attribute\u003C/code>：頂点ごとに異なる値を渡すための変数。頂点の位置、法線、色、UV座標などに使われる。\u003C/li>\u003Cli>\u003Ccode>uniform\u003C/code>：オブジェクト全体で共通する値を渡すための変数。光の向き、カメラの視点、時間、変換行列などを扱う。\u003C/li>\u003Cli>\u003Ccode>varying\u003C/code>：Vertex Shader\u003Cstrong> \u003C/strong>で計算した値を、Fragment Shader へ渡すための変数。\u003C/li>\u003C/ul>\u003Ch4 id=\"h5af0eb155b\">\u003Cbr>\u003Cstrong>型\u003C/strong>\u003C/h4>\u003Cp>扱う値には型があります。数値、ベクトル、行列、テクスチャーなど、用途に応じて使いわけます。\u003C/p>\u003Cul>\u003Cli>\u003Ccode>float\u003C/code>：小数。\u003C/li>\u003Cli>\u003Ccode>int\u003C/code>：整数。\u003C/li>\u003Cli>\u003Ccode>bool\u003C/code>：真偽値。\u003C/li>\u003Cli>\u003Ccode>vec2\u003C/code>\u003Cstrong> / \u003C/strong>\u003Ccode>vec3\u003C/code>\u003Cstrong> / \u003C/strong>\u003Ccode>vec4\u003C/code>：2〜4次元のベクトル。\u003C/li>\u003Cli>\u003Ccode>mat2\u003C/code>\u003Cstrong> / \u003C/strong>\u003Ccode>mat3\u003C/code>\u003Cstrong> / \u003C/strong>\u003Ccode>mat4\u003C/code>：2〜4次の正方行列。\u003C/li>\u003Cli>\u003Ccode>sampler2D\u003C/code>：テクスチャー。\u003C/li>\u003Cli>\u003Ccode>void\u003C/code>：何も返さないことを示す型。\u003C/li>\u003C/ul>\u003Ch4 id=\"hd2f7ea18d2\">\u003Cbr>\u003Cstrong>組み込み変数\u003C/strong>\u003C/h4>\u003Cp>用途が決まっている組み込み変数もあります。\u003C/p>\u003Cul>\u003Cli>\u003Ccode>gl_Position\u003C/code>：Vertex Shader で使用。頂点の最終的な位置を指定する。\u003C/li>\u003Cli>\u003Ccode>gl_FragCoord\u003C/code>：Fragment Shader で使用。現在処理しているフラグメントの画面上の座標を取得する。\u003C/li>\u003Cli>\u003Ccode>gl_FragColor\u003C/code>：Fragment Shader で使用。出力する色を指定する。\u003C/li>\u003C/ul>\u003Ch4 id=\"hd56e4caf46\">\u003Cbr>\u003Cstrong>関数\u003C/strong>\u003C/h4>\u003Cp>GLSL でも、他のプログラミング言語と同じように関数を作成できます。\u003C/p>\u003Cpre>\u003Ccode class=\"language-glsl\">float hoge() {\n  float a = 1.0;\n  float b = 2.0;\n  \n  return a + b;\n}\n\nfloat hogehoge(float a, float b) {\n  return a + b;\n}\u003C/code>\u003C/pre>\u003Cp>シェーダー内には \u003Ccode>main\u003C/code> \u003Cstrong>関数が必須\u003C/strong>で、この関数が自動的に呼び出されます。\u003Cbr>Vertex Shader では \u003Ccode>gl_Position\u003C/code> に頂点の位置を入れることで、描画に使う最終的な座標を決めます。\u003C/p>\u003Cpre>\u003Ccode class=\"language-glsl\">attribute vec3 position;\n\nvoid main() {\n  gl_Position = vec4(position, 1.0);\n}\u003C/code>\u003C/pre>\u003Cp>また、\u003Ccode>sin\u003C/code>、\u003Ccode>cos\u003C/code>、\u003Ccode>max\u003C/code>、\u003Ccode>min\u003C/code>、\u003Ccode>pow\u003C/code>、\u003Ccode>exp\u003C/code>、\u003Ccode>mod\u003C/code>、\u003Ccode>clamp\u003C/code>、\u003Ccode>cross\u003C/code>、\u003Ccode>dot\u003C/code>、\u003Ccode>mix\u003C/code>、\u003Ccode>step\u003C/code>、\u003Ccode>smoothstep\u003C/code>、\u003Ccode>length\u003C/code>、\u003Ccode>distance\u003C/code>、\u003Ccode>reflect\u003C/code>、\u003Ccode>refract\u003C/code>、\u003Ccode>normalize\u003C/code> などの組み込み関数も用意されています。\u003C/p>\u003Ch2 id=\"h01aea3682e\">Vertex Shader の基本\u003C/h2>\u003Cp>Vertex Shader の役割は、\u003Cstrong>3D 空間にあるジオメトリの各頂点を、2D のキャンバス座標に変換すること\u003C/strong>です。\u003C/p>\u003Cpre>\u003Ccode class=\"language-glsl\">uniform mat4 modelMatrix;\nuniform mat4 viewMatrix;\nuniform mat4 projectionMatrix;\n\nattribute vec3 position;\n\nvoid main() {\n  gl_Position = projectionMatrix * viewMatrix * modelMatrix * vec4(position, 1.0);\n}\u003C/code>\u003C/pre>\u003Cp>ここで使われている \u003Ccode>position\u003C/code> は、\u003Cstrong>ジオメトリが持つ頂点のローカル座標\u003C/strong>です。\u003Cbr>\u003Ccode>vec3\u003C/code> なので、値は \u003Ccode>x\u003C/code>、\u003Ccode>y\u003C/code>、\u003Ccode>z\u003C/code> の3つ。最終的に \u003Ccode>gl_Position\u003C/code> に入れるため、\u003Ccode>vec4(position, 1.0)\u003C/code> として4次元の値に変換します。\u003C/p>\u003Ch3 id=\"hcb28cbeadd\">座標変換の流れ\u003C/h3>\u003Cp>頂点の座標は、\u003Cstrong>行列を掛けることで段階的に変換\u003C/strong>されます。\u003C/p>\u003Cul>\u003Cli>\u003Ccode>modelMatrix\u003C/code>：ローカル座標を、ワールド座標に変換する。\u003C/li>\u003Cli>\u003Ccode>viewMatrix\u003C/code>：ワールド座標を、カメラから見たビュー座標に変換する。\u003C/li>\u003Cli>\u003Ccode>projectionMatrix\u003C/code>：ビュー座標を、画面に描画できるクリップ座標に変換する。\u003C/li>\u003C/ul>\u003Cp>つまり、\u003Cstrong>オブジェクト自身の中での位置を、3D空間全体の中での位置へ変換し、さらにカメラから見た位置へ移す。最後に、画面上に表示できる形へ投影\u003C/strong>していく流れです。\u003C/p>\u003Ch3 id=\"hf5da7b09f6\">3 つの空間\u003C/h3>\u003Cp>座標変換では、いくつかの空間を通ります。\u003C/p>\u003Cul>\u003Cli>\u003Cstrong>ワールド空間\u003C/strong>\u003Cbr>3D シーン全体の中で、オブジェクトがどこにあるかを表す空間。\u003C/li>\u003Cli>\u003Cstrong>ビュー空間\u003C/strong>\u003Cbr>カメラから見たときに、物体がどの方向にあり、どれくらい離れているかを扱う空間。\u003C/li>\u003Cli>\u003Cstrong>クリップ空間\u003C/strong>\u003Cbr>画面に映る範囲かどうかを判断するための空間。範囲外にあるものはクリッピングされ、描画対象から外れる。\u003C/li>\u003C/ul>\u003Cp>ビュー空間からクリップ空間への変換では、カメラから見た視錐台の空間を、GPU が扱いやすい立方体の空間へ変換します。文章だけだとイメージしづらい部分なので、図で整理すると次のようになります。\u003C/p>\u003Cfigure>\u003Cimg src=\"https://images.microcms-assets.io/assets/0eb919b93c74437dbd287ab903d9ea2c/57a7a6e3905c463d8fdcd216fdd11da6/%E3%83%93%E3%83%A5%E3%83%BC%E7%A9%BA%E9%96%93%E3%81%8B%E3%82%89%E3%82%AF%E3%83%AA%E3%83%83%E3%83%97%E7%A9%BA%E9%96%93%E3%81%B8%E3%81%AE%E5%A4%89%E6%8F%9B%E3%82%A4%E3%83%A1%E3%83%BC%E3%82%B8.png?w=585&amp;h=780\" alt=\"ビュー空間からクリップ空間への変換イメージ\" width=\"585\" height=\"780\">\u003C/figure>\u003Cp>行列の中身まで細かく追うと難しくなりますが、まずは\u003Cstrong>「3D 空間の頂点を、画面に映すために段階的に変換している」\u003C/strong>と捉えておくとわかりやすいです。\u003C/p>\u003Ch3 id=\"h6c59ac702c\">行列をわける理由\u003C/h3>\u003Cp>\u003Ccode>modelMatrix\u003C/code>、\u003Ccode>viewMatrix\u003C/code>、\u003Ccode>projectionMatrix\u003C/code> がわかれているのは、\u003Cstrong>途中で制御を入れられるようにするため\u003C/strong>です。\u003Cbr>たとえば、\u003Ccode>modelMatrix\u003C/code> を掛けたあとの座標を \u003Ccode>modelPosition\u003C/code> として保持しておけば、その時点で頂点の位置を動かせます。\u003C/p>\u003Cpre>\u003Ccode class=\"language-glsl\">uniform mat4 modelMatrix;\nuniform mat4 viewMatrix;\nuniform mat4 projectionMatrix;\n\nattribute vec3 position;\n\nvoid main() {\n  vec4 modelPosition = vec4(position, 1.0) * modelMatrix;\n  // 各頂点座標の移動などの操作はここで行う\n  modelPosition.x += 1.0; // オブジェクトを右に移動（各頂点の X 座標を 1.0 右に移動）\n  vec4 viewPosition = modelPosition * viewMatrix;\n  vec4 projectionPosition = viewPosition * projectionMatrix;\n  gl_Position = projectionPosition;\n}\n\u003C/code>\u003C/pre>\u003Cp>途中の座標を操作することで、オブジェクト全体を動かしたり、頂点ごとに位置を変えたりできます。\u003Cbr>次はこの考え方を使って、平面の頂点を動かしていきます。\u003C/p>\u003Ch2 id=\"h27b89f2a89\">Fragment Shader の基本\u003C/h2>\u003Cp>Vertex Shader が頂点の位置を計算するのに対して、\u003Cstrong>Fragment Shader は画面に描画される色を計算\u003C/strong>します。\u003C/p>\u003Cp>具体的には、\u003Cstrong>色、明るさ、透明度などをフラグメントごとに決定\u003C/strong>するものです。テクスチャーを貼り付けたり、光の反射を計算したり、画面全体にエフェクトをかけたりする処理にも使われます。\u003C/p>\u003Cp>基本形は以下です。\u003C/p>\u003Cpre>\u003Ccode class=\"language-glsl\">precision mediump float;\n\nvoid main() {\n  gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); // 不透明な赤\n}\u003C/code>\u003C/pre>\u003Ch3 id=\"h7ee14fb0cf\">precision mediump float;\u003C/h3>\u003Cp>\u003Ccode>float\u003C/code> 型の\u003Cstrong>計算精度を指定するための記述\u003C/strong>です。\u003C/p>\u003Cp>精度には、主に次の 3 種類があります。\u003C/p>\u003Cul>\u003Cli>\u003Ccode>highp\u003C/code>：最高精度。複雑な計算には向いているが、モバイル端末ではパフォーマンスに影響する場合も。\u003C/li>\u003Cli>\u003Ccode>mediump\u003C/code>：中精度。速度と画質のバランスがよく、通常はこれを使う。\u003C/li>\u003Cli>\u003Ccode>lowp\u003C/code>：低精度。一部のモバイル端末では高速に動作する場合があるが、見た目に違和感が出ることも。\u003C/li>\u003C/ul>\u003Cp>基本的には、\u003Cstrong>特別な理由がなければ \u003C/strong>\u003Ccode>mediump\u003C/code>\u003Cstrong> を使います。\u003C/strong>\u003C/p>\u003Ch3 id=\"ha26f547d90\">gl_FragColor\u003C/h3>\u003Cp>Fragment Shader から\u003Cstrong>出力する色を指定するための組み込み変数\u003C/strong>です。\u003C/p>\u003Cp>値は \u003Ccode>vec4\u003C/code> 型で、\u003Cspan style=\"color: #ff0000\">r\u003C/span>・\u003Cspan style=\"color: #69db00\">g\u003C/span>・\u003Cspan style=\"color: #0072ff\">b\u003C/span>・a(透明度) の 4 つの要素を持ちます。\u003C/p>\u003Cpre>\u003Ccode class=\"language-glsl\">gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);\u003C/code>\u003C/pre>\u003Cp>この場合は、赤を最大、緑と青をゼロ、透明度を最大にしているため、不透明な赤になります。\u003C/p>\u003Cp>それぞれの値は、基本的に \u003Ccode>0.0\u003C/code> から \u003Ccode>1.0\u003C/code> の範囲で指定。\u003Cbr>たとえば \u003Ccode>vec4(1.0, 1.0, 1.0, 1.0)\u003C/code> なら不透明な白、\u003Ccode>vec4(0.0, 0.0, 0.0, 1.0)\u003C/code> なら不透明な黒になります。\u003C/p>\u003Cp>このように Fragment Shader では、各フラグメントに対してどの色を出力するかを計算します。\u003C/p>\u003Ch2 id=\"hf02f93e60a\">実演してみる\u003C/h2>\u003Ch3 id=\"hdd5d55e749\">Three.js で表示する準備\u003C/h3>\u003Cp>実演では、Three.js 上にオブジェクトを表示しながら、少しずつシェーダーを書き換えていきます。\u003Cbr>オブジェクトを作る基本は、次の 3 つです。\u003C/p>\u003Cul>\u003Cli>\u003Cstrong>Geometry\u003C/strong>：形を決めるもの。\u003C/li>\u003Cli>\u003Cstrong>Material\u003C/strong>：見た目を決めるもの。\u003C/li>\u003Cli>\u003Cstrong>Mesh\u003C/strong>：Geometry と Material を組み合わせたもの。\u003C/li>\u003C/ul>\u003Cpre>\u003Ccode class=\"language-typescript\">geometry = new PlaneGeometry(1, 1, 32, 32);\n\nmaterial = new RawShaderMaterial({\n  vertexShader,\n  fragmentShader,\n  transparent: true,\n  side: DoubleSide,\n});\n\nmesh = new Mesh(geometry, material);\n\nif (scene) {\n  scene.add(mesh);\n}\u003C/code>\u003C/pre>\u003Cp>ここでは、\u003Ccode>PlaneGeometry\u003C/code> で平面を作り、\u003Ccode>RawShaderMaterial\u003C/code> に Vertex Shader と Fragment Shader を指定。\u003Cbr>最後に Geometry と Material を組み合わせて Mesh を作り、Scene に追加します。\u003Cbr>\u003Cbr>\u003C/p>\u003Cfigure>\u003Cimg src=\"https://images.microcms-assets.io/assets/0eb919b93c74437dbd287ab903d9ea2c/0667dd478db145e19c2aee62c209979f/object-1.png?w=600&amp;h=571\" alt=\"Geometry と Material を組み合わせて表示したオブジェクト\" width=\"600\" height=\"571\">\u003Cfigcaption>Geometry と Material を組み合わせて表示したオブジェクト\u003C/figcaption>\u003C/figure>\u003Cp>\u003Cbr>Material の設定を変えることで、色を変えたり、\u003Ccode>wireframe\u003C/code> で構成線を表示したり、\u003Ccode>map\u003C/code> で画像を貼り付けたりできます。\u003Cbr>また、表示したオブジェクトは、\u003Ccode>OrbitControls\u003C/code> を使ってマウス操作で回転・拡大縮小しながら確認することも可能です。\u003C/p>\u003Cp>\u003Cstrong>Geometry の分割数を増やすと頂点数が増える\u003C/strong>ため、変形したときの見た目は\u003Cstrong>なめらか\u003C/strong>になります。ただし、その分\u003Cstrong>処理は重くなる\u003C/strong>ので注意が必要です。\u003C/p>\u003Ch3 id=\"hb864bed67b\">頂点の座標を変更してみる\u003C/h3>\u003Cp>ここからは、Vertex Shader の中で頂点の座標を変更してみましょう。\u003C/p>\u003Cp>まず使うのは、\u003Ccode>modelMatrix\u003C/code> を掛けたあとの座標です。この座標を \u003Ccode>modelPosition\u003C/code> として変数に入れておくと、\u003Ccode>x\u003C/code>、\u003Ccode>y\u003C/code>、\u003Ccode>z\u003C/code> の値を変更できます。\u003C/p>\u003Cpre>\u003Ccode class=\"language-glsl\">void main() {\n  vec4 modelPosition = modelMatrix * vec4(position, 1.0);\n  modelPosition.y += 1.0;\n\n  vec4 viewPosition = viewMatrix * modelPosition;\n  vec4 projectionPosition = projectionMatrix * viewPosition;\n\n  gl_Position = projectionPosition;\n}\u003C/code>\u003C/pre>\u003Cp>この例では、\u003Ccode>modelPosition.y\u003C/code> に \u003Ccode>1.0\u003C/code> を足しています。\u003Ccode>y\u003C/code> は上下方向の座標なので、頂点は上方向へ移動します。\u003Cbr>\u003Cbr>ただし、Vertex Shader はオブジェクト全体に一度だけ実行されるものではありません。Geometry を構成する\u003Cstrong>各頂点ごとに実行\u003C/strong>されます。\u003Cbr>そのため、\u003Cstrong>すべての頂点に同じ処理がかかり、結果としてオブジェクト全体が上に移動する\u003C/strong>という仕組みです。\u003C/p>\u003Cp>次は、すべての頂点を同じ方向へ動かすのではなく、頂点ごとに移動量を変えてみます。\u003C/p>\u003Cpre>\u003Ccode class=\"language-glsl\">void main() {\n  vec4 modelPosition = modelMatrix * vec4(position, 1.0);\n  modelPosition.z += sin(modelPosition.x * 10.0) * 0.1;\n\n  vec4 viewPosition = viewMatrix * modelPosition;\n  vec4 projectionPosition = projectionMatrix * viewPosition;\n\n  gl_Position = projectionPosition;\n}\u003C/code>\u003C/pre>\u003Cfigure>\u003Cimg src=\"https://images.microcms-assets.io/assets/0eb919b93c74437dbd287ab903d9ea2c/08133f5d602a43b49d2643d3970bc46f/object-2.png?w=600&amp;h=638\" alt=\"sin を使って、頂点ごとに Z 座標の移動量を変えた状態\" width=\"600\" height=\"638\">\u003Cfigcaption>sin を使って、頂点ごとに Z 座標の移動量を変えた状態\u003C/figcaption>\u003C/figure>\u003Cp>\u003C/p>\u003Cp>\u003Ccode>modelPosition.x\u003C/code> は、各頂点が持っている X 座標です。左側の頂点と右側の頂点では X 座標が異なるため、\u003Ccode>sin\u003C/code> の計算結果も変わります。\u003Cbr>その結果、頂点ごとに Z 座標の移動量が変わり、\u003Cstrong>平面に波のような起伏\u003C/strong>が生まれます。\u003C/p>\u003Cp>\u003Ccode>modelPosition.x * 10.0\u003C/code> は、波の細かさを調整するための値です。\u003Cstrong>値を大きくすると波の間隔は細かくなり、小さくするとゆるやかな変化\u003C/strong>になります。\u003C/p>\u003Cp>最後の \u003Ccode>* 0.1\u003C/code> は、Z 方向への動きが大きくなりすぎないようにするための調整です。\u003Cbr>ここではまず、X 座標を使って Z 座標を変化させています。\u003C/p>\u003Ch3 id=\"hf4bfbf59d2\">uniform で値を渡して動かす\u003C/h3>\u003Cp>先ほどのコードでは、波の細かさを \u003Ccode>10.0\u003C/code> という固定値で指定していました。\u003C/p>\u003Cpre>\u003Ccode class=\"language-glsl\">modelPosition.z += sin(modelPosition.x * 10.0) * 0.1;\u003C/code>\u003C/pre>\u003Cp>このままだと、波の細かさを変えるたびにシェーダーのコードを書き換える必要があります。\u003Cbr>そこで使うのが \u003Ccode>uniform\u003C/code> です。\u003C/p>\u003Cp>\u003Ccode>uniform\u003C/code> を使うと、JavaScript 側からシェーダーへ値を渡せます。\u003C/p>\u003Cpre>\u003Ccode class=\"language-ts\">material = new RawShaderMaterial({\n  vertexShader,\n  fragmentShader,\n  uniforms: {\n    uFrequency: new Uniform(new Vector2(10, 5)),\n  },\n});\u003C/code>\u003C/pre>\u003Cp>Vertex Shader 側では、渡された \u003Ccode>uFrequency\u003C/code> を使って計算します。\u003C/p>\u003Cpre>\u003Ccode class=\"language-glsl\">uniform vec2 uFrequency;\n\nvoid main() {\n  vec4 modelPosition = modelMatrix * vec4(position, 1.0);\n\n  modelPosition.z += sin(modelPosition.x * uFrequency.x) * 0.1;\n  modelPosition.z += sin(modelPosition.y * uFrequency.y) * 0.1;\n\n  vec4 viewPosition = viewMatrix * modelPosition;\n  vec4 projectionPosition = projectionMatrix * viewPosition;\n\n  gl_Position = projectionPosition;\n}\u003C/code>\u003C/pre>\u003Cp>\u003Ccode>uFrequency.x\u003C/code> は X 方向、\u003Ccode>uFrequency.y\u003C/code> は Y 方向の波の細かさを調整する値です。\u003Cbr>固定値を直接書くのではなく \u003Ccode>uniform\u003C/code> にしておくことで、\u003Cstrong>外側から値を変更\u003C/strong>できるようになります。\u003C/p>\u003Cp>値の調整には、\u003Cstrong>GUI のコントローラー\u003C/strong>も使えます。\u003C/p>\u003Cpre>\u003Ccode class=\"language-ts\">gui.add(material.uniforms.uFrequency.value, &apos;x&apos;)\n  .min(0)\n  .max(20)\n  .step(0.1)\n  .name(&apos;Frequency x&apos;);\n\ngui.add(material.uniforms.uFrequency.value, &apos;y&apos;)\n  .min(0)\n  .max(20)\n  .step(0.1)\n  .name(&apos;Frequency y&apos;);\u003C/code>\u003C/pre>\u003Cp>これにより、コードを書き換えなくても、画面上のコントローラーから波の細かさを調整できます。\u003C/p>\u003Ch3 id=\"h917ebd2821\">時間を渡してアニメーションさせる\u003C/h3>\u003Cp>ここまでの波は、あくまで「波打った形」になっているだけです。\u003Cbr>\u003Cstrong>時間に合わせて動かすには、経過時間をシェーダーに\u003C/strong>渡します。\u003C/p>\u003Cp>まず、\u003Ccode>uTime\u003C/code> を \u003Ccode>uniform\u003C/code> として追加します。\u003C/p>\u003Cpre>\u003Ccode class=\"language-ts\">material = new RawShaderMaterial({\n  vertexShader,\n  fragmentShader,\n  uniforms: {\n    uFrequency: new Uniform(new Vector2(10, 5)),\n    uTime: new Uniform(0),\n  },\n});\u003C/code>\u003C/pre>\u003Cp>Three.js 側では、\u003Ccode>Clock\u003C/code> を使って\u003Cstrong>経過時間を取得\u003C/strong>します。\u003Cbr>その値を描画ループの中で \u003Ccode>uTime\u003C/code> に入れることで、フレームごとに新しい時間をシェーダーへ渡せます。\u003C/p>\u003Cpre>\u003Ccode class=\"language-ts\">const elapsedTime = clock.getElapsedTime();\n\nif (material) {\n  material.uniforms.uTime.value = elapsedTime;\n}\u003C/code>\u003C/pre>\u003Cp>Vertex Shader 側では、\u003Ccode>sin\u003C/code> の計算に \u003Ccode>uTime\u003C/code> を加えます。\u003C/p>\u003Cpre>\u003Ccode class=\"language-glsl\">uniform vec2 uFrequency;\nuniform float uTime;\n\nvoid main() {\n  vec4 modelPosition = modelMatrix * vec4(position, 1.0);\n\n  modelPosition.z += sin(modelPosition.x * uFrequency.x + uTime) * 0.1;\n  modelPosition.z += sin(modelPosition.y * uFrequency.y + uTime) * 0.1;\n\n  vec4 viewPosition = viewMatrix * modelPosition;\n  vec4 projectionPosition = projectionMatrix * viewPosition;\n\n  gl_Position = projectionPosition;\n}\u003C/code>\u003C/pre>\u003Cp>\u003Ccode>uTime\u003C/code> の値が少しずつ変わることで、\u003Ccode>sin\u003C/code> の計算結果も変化します。\u003Cbr>その結果、平面が時間に合わせてゆらゆらと波打つように動きます。\u003Cbr>\u003Cbr>\u003C/p>\u003Cdiv style=\"left: 0; width: 100%; height: 0; position: relative; padding-bottom: 56.25%;\">\u003Ciframe src=\"https://www.youtube.com/embed/Io2uTgc4CNU?rel=0\" style=\"top: 0; left: 0; width: 100%; height: 100%; position: absolute; border: 0;\" allowfullscreen scrolling=\"no\" allow=\"accelerometer *; clipboard-write *; encrypted-media *; gyroscope *; picture-in-picture *; web-share *;\" referrerpolicy=\"strict-origin\">\u003C/iframe>\u003C/div>\u003Ch3 id=\"h57a861c8ea\">波の高さを色に使う\u003C/h3>\u003Cp>次に、\u003Cstrong>波の高さ\u003C/strong>を色の計算にも使ってみましょう。\u003C/p>\u003Cp>今は、\u003Ccode>sin\u003C/code> で計算した値をそのまま \u003Ccode>modelPosition.z\u003C/code> に足しています。\u003C/p>\u003Cpre>\u003Ccode class=\"language-glsl\">modelPosition.z += sin(modelPosition.x * uFrequency.x + uTime) * 0.1;\nmodelPosition.z += sin(modelPosition.y * uFrequency.y + uTime) * 0.1;\u003C/code>\u003C/pre>\u003Cp>このままだと、波の高さは Vertex Shader の中だけで使われます。\u003Cbr>\u003Cstrong>Fragment Shader 側でも同じ値を使いたい\u003C/strong>ので、まずは高さの計算結果を \u003Ccode>elevation\u003C/code> という変数に切り出します。\u003C/p>\u003Cpre>\u003Ccode class=\"language-glsl\">varying float vElevation;\n\nvoid main() {\n  vec4 modelPosition = modelMatrix * vec4(position, 1.0);\n\n  float elevation = sin(modelPosition.x * uFrequency.x + uTime) * 0.1;\n  elevation += sin(modelPosition.y * uFrequency.y + uTime) * 0.1;\n\n  modelPosition.z += elevation;\n\n  vec4 viewPosition = viewMatrix * modelPosition;\n  vec4 projectionPosition = projectionMatrix * viewPosition;\n\n  gl_Position = projectionPosition;\n\n  vElevation = elevation;\n}\u003C/code>\u003C/pre>\u003Cp>\u003Ccode>elevation\u003C/code> に入れているのは、Z 座標を動かすために使っていた値です。\u003Cbr>この値を \u003Ccode>modelPosition.z\u003C/code> に足せば、これまでと同じように平面が波打ちます。\u003C/p>\u003Cp>さらに、\u003Ccode>vElevation\u003C/code> に代入することで、Vertex Shader で計算した高さを Fragment Shader に渡せるようになります。\u003C/p>\u003Cp>ここで使っている \u003Ccode>varying\u003C/code> は、\u003Cstrong>Vertex Shader から Fragment Shader へ値を渡すための変数\u003C/strong>です。Vertex Shader 側で代入し、Fragment Shader 側で同じ名前と型を宣言して受け取ります。\u003C/p>\u003Cpre>\u003Ccode class=\"language-glsl\">precision mediump float;\n\nvarying float vElevation;\n\nvoid main() {\n  float red = vElevation + 0.5;\n  gl_FragColor = vec4(red, 0.0, 0.0, 1.0);\n}\u003C/code>\u003C/pre>\u003Cp>\u003Ccode>gl_FragColor\u003C/code> は、\u003Ccode>r\u003C/code>、\u003Ccode>g\u003C/code>、\u003Ccode>b\u003C/code>、\u003Ccode>a\u003C/code> の4つの値で色を指定します。\u003Cbr>ここでは、\u003Ccode>vElevation\u003C/code> を\u003Cstrong>赤の値\u003C/strong>に使っています。\u003C/p>\u003Cp>ただし、\u003Ccode>elevation\u003C/code> はマイナスの値になることもあります。色の値としてそのまま使うと暗くなりすぎるため、\u003Ccode>0.5\u003C/code> を足して、\u003Cstrong>色の変化\u003C/strong>が見えるようにしています。\u003Cbr>\u003Cbr>\u003C/p>\u003Cdiv style=\"left: 0; width: 100%; height: 0; position: relative; padding-bottom: 56.25%;\">\u003Ciframe src=\"https://www.youtube.com/embed/ojY_FAOo3Ps?rel=0\" style=\"top: 0; left: 0; width: 100%; height: 100%; position: absolute; border: 0;\" allowfullscreen scrolling=\"no\" allow=\"accelerometer *; clipboard-write *; encrypted-media *; gyroscope *; picture-in-picture *; web-share *;\" referrerpolicy=\"strict-origin\">\u003C/iframe>\u003C/div>\u003Cp>\u003Cbr>これで、波の高い部分と低い部分で赤の強さが変わるようになります。\u003Cbr>頂点の位置を動かすだけでなく、その計算結果を色にも使うことで、\u003Cstrong>立体感のある表現\u003C/strong>につなげられます。\u003C/p>\u003Ch3 id=\"h8f7a0729da\">テクスチャーを貼る\u003C/h3>\u003Cp>次に、平面に画像を\u003Cstrong>テクスチャー\u003C/strong>として貼り付けてみます。\u003C/p>\u003Cp>まず、JavaScript 側で画像を読み込み、\u003Ccode>uTexture\u003C/code> という \u003Ccode>uniform\u003C/code> としてシェーダーに渡します。\u003C/p>\u003Cpre>\u003Ccode class=\"language-ts\">textureLoader = new TextureLoader();\nconst texture = textureLoader.load(&apos;/flag.jpg&apos;);\n\nmaterial = new RawShaderMaterial({\n  vertexShader,\n  fragmentShader,\n  uniforms: {\n    uFrequency: new Uniform(new Vector2(10, 5)),\n    uTime: new Uniform(0),\n    uTexture: new Uniform(texture),\n  },\n});\u003C/code>\u003C/pre>\u003Cp>\u003Ccode>uTexture\u003C/code> は、シェーダー内で使うテクスチャー画像です。\u003Cbr>ただし、画像を渡すだけでは、どの位置に画像のどの部分を貼ればよいかわかりません。\u003C/p>\u003Cp>そこで使うのが \u003Ccode>uv\u003C/code> です。\u003C/p>\u003Cp>\u003Ccode>uv\u003C/code> は、Geometry が持っている\u003Cstrong>テクスチャー用の座標\u003C/strong>です。\u003Cbr>各頂点に対して、画像のどの位置を対応させるかを表しています。\u003C/p>\u003Cp>Vertex Shader 側では、Geometry が持つ \u003Ccode>uv\u003C/code> を \u003Ccode>attribute\u003C/code> として受け取り、\u003Ccode>vUv\u003C/code> として Fragment Shader に渡します。\u003C/p>\u003Cpre>\u003Ccode class=\"language-glsl\">attribute vec2 uv;\n\nvarying float vElevation;\nvarying vec2 vUv;\n\nvoid main() {\n  vec4 modelPosition = modelMatrix * vec4(position, 1.0);\n\n  float elevation = sin(modelPosition.x * uFrequency.x + uTime) * 0.1;\n  elevation += sin(modelPosition.y * uFrequency.y + uTime) * 0.1;\n\n  modelPosition.z += elevation;\n\n  vec4 viewPosition = viewMatrix * modelPosition;\n  vec4 projectionPosition = projectionMatrix * viewPosition;\n\n  gl_Position = projectionPosition;\n\n  vElevation = elevation;\n  vUv = uv;\n}\u003C/code>\u003C/pre>\u003Cp>\u003Ccode>uv\u003C/code> は Vertex Shader では使えますが、そのまま Fragment Shader から参照することはできません。\u003Cbr>そのため、\u003Ccode>varying\u003C/code> の \u003Ccode>vUv\u003C/code> に代入して、\u003Cstrong>Fragment Shader 側へ渡します\u003C/strong>。\u003C/p>\u003Cp>Fragment Shader 側では、同じ \u003Ccode>vUv\u003C/code> を宣言して受け取ります。\u003C/p>\u003Cpre>\u003Ccode class=\"language-glsl\">precision mediump float;\nuniform sampler2D uTexture;\nvarying vec2 vUv;\n\nvoid main() {\n  vec4 textureColor = texture2D(uTexture, vUv);\n  gl_FragColor = textureColor;\n}\u003C/code>\u003C/pre>\u003Cp>\u003Ccode>texture2D(uTexture, vUv)\u003C/code> は、\u003Ccode>uTexture\u003C/code> の中から \u003Ccode>vUv\u003C/code> の座標に対応する色を取得する処理です。\u003Cbr>取得した色を \u003Ccode>gl_FragColor\u003C/code> に入れることで、\u003Cstrong>平面に画像が表示されます\u003C/strong>。\u003C/p>\u003Cp>補足として、\u003Ccode>vUv\u003C/code> をそのまま \u003Ccode>gl_FragColor\u003C/code> に入れると、UV 座標の変化をグラデーションとして確認できます。\u003Cbr>\u003Cbr>\u003C/p>\u003Cdiv style=\"left: 0; width: 100%; height: 0; position: relative; padding-bottom: 56.25%;\">\u003Ciframe src=\"https://www.youtube.com/embed/zqOtYtCk-S0?rel=0\" style=\"top: 0; left: 0; width: 100%; height: 100%; position: absolute; border: 0;\" allowfullscreen scrolling=\"no\" allow=\"accelerometer *; clipboard-write *; encrypted-media *; gyroscope *; picture-in-picture *; web-share *;\" referrerpolicy=\"strict-origin\">\u003C/iframe>\u003C/div>\u003Cp>\u003Cbr>これで、Fragment Shader 側で画像の色を取得し、各フラグメントの色として出力できるようになります。\u003Cbr>次は、このテクスチャーの色に \u003Ccode>vElevation\u003C/code> を掛けて、\u003Cstrong>波の高さに応じた明暗\u003C/strong>を加えていきます。\u003C/p>\u003Ch3 id=\"hf29ce76e4e\">テクスチャーに明暗を加える\u003C/h3>\u003Cp>テクスチャーの色に \u003Ccode>vElevation\u003C/code> を反映して、波の高さに応じた\u003Cstrong>明暗\u003C/strong>を加えてみます。\u003C/p>\u003Cp>先ほどテクスチャーを貼ったときは、\u003Ccode>texture2D(uTexture, vUv)\u003C/code> で取得した色を、そのまま \u003Ccode>gl_FragColor\u003C/code> に渡していました。\u003C/p>\u003Cpre>\u003Ccode class=\"language-glsl\">vec4 textureColor = texture2D(uTexture, vUv);\n\ngl_FragColor = textureColor;\n\u003C/code>\u003C/pre>\u003Cp>ここに、波の高さとして受け取っている \u003Ccode>vElevation\u003C/code> を掛けます。\u003C/p>\u003Cpre>\u003Ccode class=\"language-glsl\">precision mediump float;\nuniform sampler2D uTexture;\nvarying float vElevation;\nvarying vec2 vUv;\n\nvoid main() {\n  vec4 textureColor = texture2D(uTexture, vUv);\n  textureColor.rgb *= vElevation * 1.5 + 0.5;\n  gl_FragColor = textureColor;\n}\n\u003C/code>\u003C/pre>\u003Cp>\u003Ccode>textureColor.rgb\u003C/code> は、テクスチャーの \u003Ccode>r\u003C/code>、\u003Ccode>g\u003C/code>、\u003Ccode>b\u003C/code> の値です。\u003Cbr>ここに \u003Ccode>vElevation\u003C/code> を使った値を掛けることで、波の高い部分と低い部分で明るさが変わります。\u003C/p>\u003Cp>\u003Ccode>vElevation * 1.5 + 0.5\u003C/code> のうち、\u003Ccode>1.5\u003C/code> は\u003Cstrong>明暗の差を強めるための値\u003C/strong>です。\u003Cbr>\u003Ccode>0.5\u003C/code> は、\u003Cstrong>全体が暗くなりすぎないようにするための調整\u003C/strong>です。\u003C/p>\u003Cp>これで、ただ画像を貼るだけでなく、\u003Cstrong>波の動きに合わせてテクスチャーの見え方も変化\u003C/strong>するようになります。\u003Cbr>Vertex Shader で計算した高さを Fragment Shader の色計算にも使うことで、より立体感のある表現に近づけられます。\u003C/p>\u003Ch3 id=\"hbc3f367c71\">応用例：GPGPU と Flow Field\u003C/h3>\u003Cp>シェーダーを応用すると、大量のパーティクルを動かすような表現にもつなげられます。\u003Cbr>その一例が、\u003Ca href=\"https://ja.wikipedia.org/wiki/GPGPU\" target=\"_blank\" rel=\"noopener noreferrer\">\u003Cstrong>GPGPU\u003C/strong>\u003C/a> と \u003Cstrong>Flow Field\u003C/strong> を使った表現です。\u003C/p>\u003Cp>\u003Cstrong>GPGPU は、描画だけでなく計算処理にも GPU を活用する手法\u003C/strong>です。\u003Cbr>\u003Cstrong>Flow Field は、空間上に「どの方向へ進むか」という流れを作り、パーティクルをその流れに沿って動かす考え方\u003C/strong>です。\u003Cbr>\u003Cbr>\u003C/p>\u003Cdiv style=\"left: 0; width: 100%; height: 0; position: relative; padding-bottom: 56.25%;\">\u003Ciframe src=\"https://www.youtube.com/embed/4IZ-A3z9xZ4?rel=0\" style=\"top: 0; left: 0; width: 100%; height: 100%; position: absolute; border: 0;\" allowfullscreen scrolling=\"no\" allow=\"accelerometer *; clipboard-write *; encrypted-media *; gyroscope *; picture-in-picture *; web-share *;\" referrerpolicy=\"strict-origin\">\u003C/iframe>\u003C/div>\u003Cp>\u003Cbr>このようなパーティクル表現は、\u003Cstrong>以前ご紹介したピエール・エルメ・パリ「NOËL」特設サイトの演出\u003C/strong>にも使われています。\u003Cbr>\u003Cbr>\u003C/p>\u003Cdiv class=\"iframely-embed\">\u003Cdiv class=\"iframely-responsive\" style=\"height: 140px; padding-bottom: 0;\">\u003Ca href=\"https://www.s2factory.co.jp/blog/noel2025/\" data-iframely-url=\"https://iframely.net/api/iframe?card=small&amp;url=https%3A%2F%2Fwww.s2factory.co.jp%2Fblog%2Fnoel2025%2F&amp;key=c271a3ec77ff4aa44d5948170dd74161\">\u003C/a>\u003C/div>\u003C/div>\u003Cscript async src=\"https://iframely.net/embed.js\" charset=\"utf-8\">\u003C/script>\u003Ch2 id=\"ha214098e44\">まとめ\u003C/h2>\u003Cp>シェーダーを使うと、頂点の位置やフラグメントの色を自分で計算し、Three.js の標準 Material だけでは難しい表現を作ることができます。\u003C/p>\u003Cp>今回は、\u003Cstrong>Vertex Shader で平面の頂点を動かし、\u003C/strong>\u003Ccode>uniform\u003C/code>\u003Cstrong> で時間を渡してアニメーション\u003C/strong>させました。\u003Cbr>さらに、その高さ情報を Fragment Shader に渡すことで、\u003Cstrong>色やテクスチャーの見え方にも反映\u003C/strong>しています。\u003C/p>\u003Cp>GLSL の記述や座標変換など、最初は難しく感じる部分もあります。\u003Cbr>一方で、\u003Cstrong>少ないコードでも見た目に大きな変化を出せるのが、シェーダーの面白いところ\u003C/strong>です。\u003C/p>\u003Cp>さらに発展させると、今回最後に触れたような大量のパーティクル表現にもつながります。\u003Cbr>このあたりは奥が深いので、また別の機会に詳しく紹介できればと思います。\u003C/p>",{"url":11,"height":12,"width":13},"https://images.microcms-assets.io/assets/0eb919b93c74437dbd287ab903d9ea2c/3651571c58a740eaaa57321b312e1503/%E3%82%B7%E3%82%A7%E3%83%BC%E3%83%80%E3%83%BC.png",895,1444,[15,21,26,30],{"id":16,"createdAt":17,"updatedAt":18,"publishedAt":17,"revisedAt":17,"name":19,"top":20},"frontend","2023-06-01T09:41:44.362Z","2023-08-09T06:32:01.317Z","フロントエンド",false,{"id":22,"createdAt":23,"updatedAt":24,"publishedAt":23,"revisedAt":23,"name":25,"top":20},"animation","2023-06-01T09:41:14.747Z","2023-08-09T06:39:18.585Z","アニメーション",{"id":27,"createdAt":28,"updatedAt":28,"publishedAt":28,"revisedAt":28,"name":29,"top":20},"tech","2023-06-01T09:42:09.082Z","技術",{"id":31,"createdAt":32,"updatedAt":33,"publishedAt":34,"revisedAt":33,"name":35,"top":36},"tea","2023-06-01T09:41:19.770Z","2025-04-22T12:20:26.123Z","2023-07-28T06:30:00.000Z","お茶会",true,"Vertex Shader と Fragment Shader の基本から、頂点の変形、色の制御、テクスチャー、GPGPU を使ったパーティクル表現まで、Three.js の実装例を通してシェーダーによるWeb表現の仕組みを紹介します。",[39,65,85,104,121,139],{"id":40,"createdAt":41,"updatedAt":42,"publishedAt":43,"revisedAt":42,"title":44,"content2":45,"eyecatch":46,"tag":50,"heading_list":20,"recruit":20,"is_html":20,"description":62,"related_articles":63,"recommended_articles":64,"is_migration":20},"noel2025","2026-01-08T07:37:35.535Z","2026-05-12T01:12:30.120Z","2026-03-10T00:38:35.975Z","NOËL 2025 特設サイト 制作振り返り","\u003Cp>弊社は、これまで\u003Ca href=\"https://www.pierreherme.co.jp/\" target=\"_blank\" rel=\"noopener noreferrer\">\u003Cstrong>ピエール・エルメ\u003C/strong>\u003C/a>の Web サイト制作に、デザインと実装の両面から継続的に携わってきました。\u003Cbr>年末に公開される「NOËL」のマカロンのサイトは、その中でも毎年スペシャルサイトとして制作しているページです。\u003C/p>\u003Cp>2025年も過去の延長ではなく、あらためて表現や体験の設計から考え直し、新たに制作しました。\u003Cbr>\u003Cstrong>深海をモチーフにした世界観\u003C/strong>や、\u003Cstrong>サイト全体を通して展開される演出\u003C/strong>など、細かな検討を重ねながら形にしています。\u003C/p>\u003Cp>本記事では、NOËL 2025 特設サイトの制作を振り返りつつ、\u003Cstrong>デザインと実装の両面から、表現や実装をどのように組み立てていったのか\u003C/strong>を紹介したいと思います。\u003Cbr>\u003Cbr>実際のサイトはこちらです。\u003C/p>\u003Cdiv class=\"iframely-embed\">\u003Cdiv class=\"iframely-responsive\" style=\"height: 140px; padding-bottom: 0;\">\u003Ca href=\"https://www.pierreherme.co.jp/feature/noel-2025/\" data-iframely-url=\"https://cdn.iframe.ly/api/iframe?card=small&amp;url=https%3A%2F%2Fwww.pierreherme.co.jp%2Ffeature%2Fnoel-2025%2F&amp;key=c271a3ec77ff4aa44d5948170dd74161\">\u003C/a>\u003C/div>\u003C/div>\u003Cscript async src=\"https://cdn.iframe.ly/embed.js\" charset=\"utf-8\">\u003C/script>\u003Ch2 id=\"h3bba4f50d6\">テーマと表現\u003C/h2>\u003Cp>今回制作した NOËL 2025 特設サイトは、年末に公開されるコンテンツとして、例年と同様に力を入れて制作したページです。\u003C/p>\u003Cp>テーマとして提示されていたのは、\u003Cstrong>「深海(アビス)」をモチーフにした世界観\u003C/strong>。\u003Cbr>\u003Cstrong>冒頭は、海面からマカロンが海底に向かって落ちていくアニメーション\u003C/strong>から始まります。そこから\u003Cstrong>スクロールに合わせて、背景のグラデーションや泡の演出が少しずつ変化\u003C/strong>していきます。\u003Cbr>\u003Cbr>要素を場面ごとに切り替えるのではなく、\u003Cstrong>同じ要素の状態を変えていく\u003C/strong>ことで、ページ全体が徐々に沈んでいく印象をつくりました。スクロール量がそのまま深さに対応する構成です。\u003Cbr>\u003Cbr>当初は逆方向の流れも想定していましたが、体験としての自然さを優先し、現在のものに落ち着きました。\u003Cbr>\u003Cbr>\u003C/p>\u003Cdiv style=\"left: 0; width: 100%; height: 0; position: relative; padding-bottom: 75%;\">\u003Ciframe src=\"https://www.youtube.com/embed/0CUs99XDKqI?rel=0\" style=\"top: 0; left: 0; width: 100%; height: 100%; position: absolute; border: 0;\" allowfullscreen scrolling=\"no\" allow=\"accelerometer *; clipboard-write *; encrypted-media *; gyroscope *; picture-in-picture *; web-share *;\" referrerpolicy=\"strict-origin\">\u003C/iframe>\u003C/div>\u003Cp>\u003Cbr>商品情報のエリアでも、背景の泡や揺らぎはそのまま残しながら、商品画像の周囲に泡のフレームを重ねています。視線を商品に集めると同時に、海の中の要素としての泡もそこで見せ続けることで、流れを断ち切らず、世界観の中でフォーカスをつくっています。\u003Cbr>\u003Cbr>また、\u003Cstrong>パッケージにも使われているサンゴのイラストが持つきらめきや質感を、Web 上でも表現できないか\u003C/strong>という要望があり、制作の中でも表現として工夫したポイントの一つです。\u003C/p>\u003Ch2 id=\"ha2fbbc5f62\">実装について\u003C/h2>\u003Cp>今回のサイトでは、砂や泡の表現を画面全体で動かしています。そのため、処理負荷を前提に実装を組み立てました。\u003C/p>\u003Cp>\u003Cstrong>砂は WebGL のシェーダーを使用し、パーティクルで構成\u003C/strong>しています。数が数万個に及ぶため、CPU では負荷が高くなり重くなってしまうため、GPU で並列処理できるように \u003Cstrong>GPGPU という技術を採用\u003C/strong>し実装しました。描画や動きの計算を GPU で処理することで、数万個のパーティクルが滑らかに動くように実装しています。\u003C/p>\u003Cp>通常のテキスト要素では、砂のアニメーションを表現できないため、\u003Cstrong>一部のテキストを 3D モデルとして作成し、WebGL で描画・配置\u003C/strong>。ただし、そのままでは検索やテキスト選択ができなくなってしまうため、\u003Cstrong>SEO 的観点\u003C/strong>からも \u003Cstrong>DOM 上には同内容のテキストも配置\u003C/strong>し、表現と情報の両立を図りました。\u003Cbr>\u003Cbr>\u003C/p>\u003Cfigure>\u003Cimg src=\"https://images.microcms-assets.io/assets/0eb919b93c74437dbd287ab903d9ea2c/34caffde445d451ca19cdef83760216e/%E7%A0%82%E3%81%AE%E8%A1%A8%E7%8F%BE.png\" alt=\"砂の表現\" width=\"1887\" height=\"1044\">\u003Cfigcaption>テキストは 3D で作成。\u003C/figcaption>\u003C/figure>\u003Cp>\u003Cbr>背景では、泡が画面下部から上部へと浮かび続けています。\u003Cbr>初めは画面全体に満遍なく漂っていますが、スクロールが進みサンゴのイラストの位置に達すると、左右へと散るようにしています。中央から泡が減ることで、自然にコンテンツへ視線が向かうようにしています。\u003C/p>\u003Cp>この泡も砂と同じく \u003Cstrong>WebGL のシェーダーで制御\u003C/strong>し、\u003Cstrong>ノイズ関数を用いて形状を変化させ、水中らしい速度や揺れ幅になるよう調整\u003C/strong>しました。\u003Cbr>\u003Cbr>\u003C/p>\u003Cdiv style=\"left: 0; width: 100%; height: 0; position: relative; padding-bottom: 75%;\">\u003Ciframe src=\"https://www.youtube.com/embed/oSIIvT5jZ-0?rel=0\" style=\"top: 0; left: 0; width: 100%; height: 100%; position: absolute; border: 0;\" allowfullscreen scrolling=\"no\" allow=\"accelerometer *; clipboard-write *; encrypted-media *; gyroscope *; picture-in-picture *; web-share *;\" referrerpolicy=\"strict-origin\">\u003C/iframe>\u003C/div>\u003Cp>\u003Cbr>一方で、\u003Cstrong>商品情報エリアで商品画像を囲む泡\u003C/strong>は、別のアプローチを取っています。こちらは以前ブログで解説した \u003Cstrong>SVG の円アニメーションを応用\u003C/strong>したものです。\u003Cbr>視線を集めるフレームとして機能させながら、水中の揺らぎとしても違和感が出ないよう、動きや歪みを調整しています。\u003C/p>\u003Cdiv class=\"iframely-embed\">\u003Cdiv class=\"iframely-responsive\" style=\"height: 140px; padding-bottom: 0;\">\u003Ca href=\"https://www.s2factory.co.jp/blog/svg-animation/\" data-iframely-url=\"https://cdn.iframe.ly/api/iframe?card=small&amp;url=https%3A%2F%2Fwww.s2factory.co.jp%2Fblog%2Fsvg-animation%2F&amp;key=c271a3ec77ff4aa44d5948170dd74161\">\u003C/a>\u003C/div>\u003C/div>\u003Cscript async src=\"https://cdn.iframe.ly/embed.js\" charset=\"utf-8\">\u003C/script>\u003Cfigure>\u003Cimg src=\"https://images.microcms-assets.io/assets/0eb919b93c74437dbd287ab903d9ea2c/dbf4059fd28646b7887aff54eb237ec5/awa.png\" alt=\"限定のテキスト\" width=\"1754\" height=\"1037\">\u003Cfigcaption>右下の限定のテキストは海藻をイメージした動きに。\u003C/figcaption>\u003C/figure>\u003Cp>\u003Cstrong>サンゴのイラストに使われている箔押しの質感を、Web上でもどう再現するか\u003C/strong>も今回のテーマでした。スクロール位置に応じて色味や干渉の見え方が変化するようにし、きらめきがわずかに揺らぐ印象をつくっています。\u003C/p>\u003Cp>この表現はサンゴのイラストだけでなく、一部のテキストにも展開しています。強いエフェクトではなく、あくまで質感として馴染ませることで、全体の世界観に統一感を持たせました。\u003Cbr>\u003Cbr>\u003C/p>\u003Cfigure>\u003Cimg src=\"https://images.microcms-assets.io/assets/0eb919b93c74437dbd287ab903d9ea2c/4685154937f147cc81fb4b135aa6b471/%E3%82%B5%E3%83%B3%E3%82%B4.png\" alt=\"サンゴのイラスト\" width=\"2204\" height=\"1704\">\u003Cfigcaption>スクロール位置によって色味が変わる。\u003C/figcaption>\u003C/figure>\u003Ch3 id=\"h20a7e56e9b\">ブラウザ差への対応\u003C/h3>\u003Cp>ブラウザごとの差も、実装を進める中で意識していた点のひとつです。\u003Cbr>SVG で表現していた部分が Safari では重くなることが判明したため、その箇所は Canvas に置き換えて、サイトのパフォーマンスが落ちないように調整しました。\u003C/p>\u003Cp>Chrome で問題なく動いていても、Safari やスマートフォンで成立しない表現は採用していません。\u003Cbr>見た目や動きの印象を変えずに、\u003Cstrong>さまざまな環境で無理なく動作\u003C/strong>するようにしています。\u003C/p>\u003Ch2 id=\"h70d3b5ba0f\">デザイナーとエンジニアの連携\u003C/h2>\u003Cp>今回の制作では、\u003Cstrong>早い段階で動きを確認できるモックを用意\u003C/strong>しました。砂や泡、揺らぎといった表現は静止画では判断できないため、密度や速度、スクロール時の変化量を実際に動かしながら決めていく必要があったからです。\u003Cbr>\u003Cbr>制作チーム内でも、完成デザインを渡してから実装するという流れではなく、\u003Cstrong>動く状態を前提\u003C/strong>に進めています。砂や泡の密度、揺らぎの強さ、スクロール時の変化量などは、実際に動かした状態で確認しながら決めていきました。静止画で固めてから実装するのではなく、挙動を見ながら細部を詰めていく進め方です。\u003C/p>\u003Cp>このモックは制作チーム内の検証だけでなく、ピエール・エルメの本社がフランスにあるため、本国側との共有にも使っています。完成形を言葉で説明するのではなく、実際の動きを見せながら認識を揃えていきました。\u003C/p>\u003Ch3 id=\"hbc6a78803c\">動きの強さのバランス\u003C/h3>\u003Cp>調整の中で、結果的に\u003Cstrong>採用しなかった表現\u003C/strong>もあります。\u003Cbr>たとえば、商品周りの揺らぎや動きについては、当初はもっと強くかけることも可能でした。\u003Cbr>\u003Cbr>ただ、動きを強めすぎると視線が演出に引っ張られ、商品そのものが見えづらくなります。そのため、動きをなくすのではなく、意識しない程度に抑えています。\u003Cbr>結果として、表現を足していくというよりも、\u003Cstrong>「どこで止めるか」を決めていく作業が多かった\u003C/strong>、という印象です。\u003C/p>\u003Ch2 id=\"ha214098e44\">まとめ\u003C/h2>\u003Cp>弊社では、社内の\u003Cstrong>デザイナーとエンジニア\u003C/strong>が普段から一緒にプロジェクトに関わり、\u003Cstrong>それぞれの視点や知見を共有\u003C/strong>しながら制作を進めています。\u003Cbr>職種は違いますが、お互いの領域を理解しているからこそ、\u003Cstrong>表現と実装を分けずに扱う\u003C/strong>ことが可能です。\u003Cbr>\u003Cbr>また、エンジニアもヒアリングの段階から直接やり取りに入り、技術的な可能性や制約を共有しながら、\u003Cstrong>「どうすれば実現できるか」\u003C/strong>を具体化できることも S2 の特長です。\u003Cbr>というのも、日々新しい技術に触れ、表現の幅を広げている土台があるからです。今回の砂や泡の表現も、その積み重ねの一つ。\u003C/p>\u003Cp>NOËL 2025 の制作も、こうした関わりの中で形になりました。\u003Cbr>\u003Cstrong>対話を重ねながら、デザインと技術の両面から最適な形を探る。\u003C/strong>それが私たちの進め方です。\u003Cbr>\u003Cbr>Web サイトの制作やリニューアルをご検討の際は、ぜひお気軽にご相談ください。\u003C/p>",{"url":47,"height":48,"width":49},"https://images.microcms-assets.io/assets/0eb919b93c74437dbd287ab903d9ea2c/e5ab4815df3942ccbfa7bd994c2233e3/1.png",1800,2400,[51,53,55,56,58,59,60],{"id":52},"promotion",{"id":54},"look-back",{"id":27},{"id":57},"design",{"id":16},{"id":22},{"id":61},"3-d","ピエール・エルメ「NOËL 2025」特設サイトの制作事例。深海をテーマにした体験設計と、WebGLやGPGPUを用いたパーティクル表現など、デザインと実装の工夫を紹介します。",[],[],{"id":66,"createdAt":67,"updatedAt":68,"publishedAt":69,"revisedAt":68,"title":70,"content2":71,"eyecatch":72,"tag":76,"heading_list":36,"recruit":20,"is_html":20,"related_articles":83,"recommended_articles":84,"is_migration":20},"visualizer","2025-06-04T08:28:31.387Z","2025-08-21T01:32:41.546Z","2025-08-13T01:27:32.063Z","Web Audio API でビジュアライザーを作った話","\u003Cp>何もしていないのにぎっくり腰になりました。\u003C/p>\u003Cp>この記事は、社内イベント「\u003Ca href=\"https://www.s2factory.co.jp/blog/tea/\" target=\"_blank\" rel=\"noopener noreferrer nofollow\">\u003Cu>お茶会\u003C/u>\u003C/a>」での発表内容をもとにまとめたものです。\u003Cbr>今回は弊社の morisaki が\u003Cstrong>「Web Audio API を使った波形の可視化」\u003C/strong>について話しました。\u003C/p>\u003Chr>\u003Cp>私は音楽は聴く専門で、楽器はリコーダーくらいしか経験がありません。\u003Cbr>でも音楽そのものは好きで、たまにライブに行ったり、野外フェスにも毎年のように参加しています。\u003Cbr>ずっと楽器に対してゆるく憧れを持ちながら、気がつけば時間だけが過ぎていきました。\u003Cbr>最近は「子どもたちと一緒に何か始めてみるのもいいかも」と思ったりしつつも、なかなか行動には移せていません。\u003Cbr>そのうちに、「電子楽器でも触ってみようかな」と思うようになります。見た目のかっこいい機材を見つけては気持ちが高まり、YouTube で動画を見漁っては、カートに入れて見送る……という日々が続いていました。\u003C/p>\u003Cp>ちょうどそんな時期に、お茶会の発表の順番がめぐってきます。\u003Cbr>せっかくだし、\u003Cstrong>JavaScript でなにか音楽っぽいことができないかな\u003C/strong>と思い立ち、「\u003Cstrong>音のビジュアライザーを作ってみた\u003C/strong>」という話です。\u003C/p>\u003Ch2 id=\"he1641f7130\">気づけば機材を調べていた\u003C/h2>\u003Cp>電子楽器に対する興味がふつふつと湧いてきた頃、気になる機材を見つけては、スペックや使い方を調べるようになっていました。\u003Cbr>その中でも特に印象に残ったモデルをいくつか紹介します。\u003C/p>\u003Ch3 id=\"h922bd77791\">YAMAHA SEQTRAK\u003C/h3>\u003Cdiv class=\"iframely-embed\">\u003Cdiv class=\"iframely-responsive\" style=\"height: 140px; padding-bottom: 0;\">\u003Ca href=\"https://jp.yamaha.com/products/music_production/music-production-studios/seqtrak/index.html\" data-iframely-url=\"https://cdn.iframe.ly/api/iframe?card=small&amp;url=https%3A%2F%2Fjp.yamaha.com%2Fproducts%2Fmusic_production%2Fmusic-production-studios%2Fseqtrak%2Findex.html&amp;key=c271a3ec77ff4aa44d5948170dd74161\">\u003C/a>\u003C/div>\u003C/div>\u003Cscript async src=\"https://cdn.iframe.ly/embed.js\" charset=\"utf-8\">\u003C/script>\u003Cp>\u003Cbr>コンパクトな筐体に、16小節分のボタンが並んだデザイン。視認性の高いインターフェースとオレンジのアクセントが印象的で、「これが家にあったらかっこいいな」と思わせてくれる見た目。\u003C/p>\u003Cp>アプリでも似たような構成のものは見かけますが、やはり物理ボタンの説得力には惹かれます。\u003Cbr>価格はおよそ55,000円。すぐに手を出すには少し勇気のいる値段です。\u003C/p>\u003Ch3 id=\"h7594457b65\">TEENAGE ENGINEERING OP–1 field\u003C/h3>\u003Cdiv class=\"iframely-embed\">\u003Cdiv class=\"iframely-responsive\" style=\"height: 140px; padding-bottom: 0;\">\u003Ca href=\"https://www.minet.jp/brand/teenageengineering/op-1-field/\" data-iframely-url=\"https://cdn.iframe.ly/api/iframe?card=small&amp;url=https%3A%2F%2Fwww.minet.jp%2Fbrand%2Fteenageengineering%2Fop-1-field%2F&amp;key=c271a3ec77ff4aa44d5948170dd74161\">\u003C/a>\u003C/div>\u003C/div>\u003Cscript async src=\"https://cdn.iframe.ly/embed.js\" charset=\"utf-8\">\u003C/script>\u003Cp>\u003Cbr>さらに調べていくうちに出会ったのが、\u003Cstrong>TEENAGE ENGINEERING \u003C/strong>というスウェーデンの電子楽器メーカー。中でも代表的なモデルのひとつが「\u003Cstrong>OP–1 field\u003C/strong>」です。\u003C/p>\u003Cp>これはいわゆるシンセサイザーで、音作り・録音・エフェクトなど一通りの操作がこれ一台で完結します。\u003Cbr>操作性や音質もさることながら、無駄のない美しいデザインに強く惹かれました。\u003C/p>\u003Cp>ただし、価格は日本円でおよそ\u003Cstrong>30万円\u003C/strong>。現実的にはなかなか手が出せるものではなく、眺めるだけで満足する“憧れの存在”といったところです。\u003C/p>\u003Ch3 id=\"h00b9c89146\">TEENAGE ENGINEERING EP–133 K.O. II\u003C/h3>\u003Cdiv class=\"iframely-embed\">\u003Cdiv class=\"iframely-responsive\" style=\"height: 140px; padding-bottom: 0;\">\u003Ca href=\"https://www.minet.jp/brand/teenageengineering/ep-133-k-o-ii/\" data-iframely-url=\"https://cdn.iframe.ly/api/iframe?card=small&amp;url=https%3A%2F%2Fwww.minet.jp%2Fbrand%2Fteenageengineering%2Fep-133-k-o-ii%2F&amp;key=c271a3ec77ff4aa44d5948170dd74161\">\u003C/a>\u003C/div>\u003C/div>\u003Cscript async src=\"https://cdn.iframe.ly/embed.js\" charset=\"utf-8\">\u003C/script>\u003Cp>\u003Cbr>もう少し手頃な選択肢として見つけたのが、「\u003Cstrong>K.O. II\u003C/strong>」と呼ばれるモデルです。\u003Cbr>こちらは「サンプラー」に分類される機材で、価格はおよそ5万円。\u003Cbr>実際にこの機材を使って制作している動画も多く、使いこなせれば楽しそうだなと思わせてくれましたが、まだ手を出すには迷う価格帯でもあります。\u003C/p>\u003Ch3 id=\"hfba45d187c\">PO-33 K.O!\u003C/h3>\u003Cdiv class=\"iframely-embed\">\u003Cdiv class=\"iframely-responsive\" style=\"height: 140px; padding-bottom: 0;\">\u003Ca href=\"https://www.minet.jp/brand/teenageengineering/po-33-k-o/\" data-iframely-url=\"https://cdn.iframe.ly/api/iframe?card=small&amp;url=https%3A%2F%2Fwww.minet.jp%2Fbrand%2Fteenageengineering%2Fpo-33-k-o%2F&amp;key=c271a3ec77ff4aa44d5948170dd74161\">\u003C/a>\u003C/div>\u003C/div>\u003Cscript async src=\"https://cdn.iframe.ly/embed.js\" charset=\"utf-8\">\u003C/script>\u003Cp>\u003Cbr>「とりあえず何か一つ触ってみよう」という気持ちで、\u003Cstrong>最終的に購入したのがこのモデル\u003C/strong>です。\u003Cbr>K.O. IIの前身にあたる機種で、電卓のような見た目をした非常にコンパクトなサンプラー。\u003Cbr>マイクとスピーカーが内蔵されており、これ単体で録音・再生ができる手軽さが魅力です。\u003C/p>\u003Cp>コラボモデルも豊富で、例えば『ストリートファイター』や『ロックマン』などとの限定版も展開されています。\u003Cbr>ときどき触っては音を鳴らして遊んでいるのですが、やはり\u003Cstrong>“音楽をつくる”というのは、想像以上に難しい\u003C/strong>。\u003C/p>\u003Ch2 id=\"h03c16353ff\">Web Audio API で“音楽っぽいこと”を\u003C/h2>\u003Cp>電子楽器の代わりに、ブラウザ上で音を扱えないかと調べていくうちに、「\u003Ca href=\"https://developer.mozilla.org/ja/docs/Web/API/Web_Audio_API\" target=\"_blank\" rel=\"noopener noreferrer\">\u003Cstrong>Web Audio API\u003C/strong>\u003C/a>」という技術にたどり着きました。JavaScript で音を合成したり、フィルターをかけたりと、\u003Cstrong>シンセサイザーのような機能を持った API \u003C/strong>です。\u003Cbr>\u003Cbr>\u003C/p>\u003Cdiv class=\"iframely-embed\">\u003Cdiv class=\"iframely-responsive\" style=\"height: 140px; padding-bottom: 0;\">\u003Ca href=\"https://developer.mozilla.org/ja/docs/Web/API/Web_Audio_API\" data-iframely-url=\"https://cdn.iframe.ly/api/iframe?card=small&amp;url=https%3A%2F%2Fdeveloper.mozilla.org%2Fja%2Fdocs%2FWeb%2FAPI%2FWeb_Audio_API&amp;key=c271a3ec77ff4aa44d5948170dd74161\">\u003C/a>\u003C/div>\u003C/div>\u003Cscript async src=\"https://cdn.iframe.ly/embed.js\" charset=\"utf-8\">\u003C/script>\u003Cp>\u003Cbr>「これを使えば、ブラウザ上で音楽っぽいことができるかもしれない」と思ったものの、実際に触れてみると、信号処理や周波数、波形など、なかなか専門的な世界。いきなり作曲やシンセを自作するには、ちょっとハードルが高すぎました。\u003C/p>\u003Cp>そこで方向転換。\u003Cstrong>同じ Web Audio API を使って、音そのものではなく“音の見た目”にフォーカスしてみよう\u003C/strong>ということで、\u003Cstrong>ビジュアライザー\u003C/strong>を作ってみることにしました。\u003C/p>\u003Ch2 id=\"hadf9293ec1\">ビジュアライザーの実装\u003C/h2>\u003Cp>シンプルな構成で、HTML も非常に軽量です。\u003C/p>\u003Ch3 id=\"h79a5c79eb6\">波形を表示するまで\u003C/h3>\u003Ch4 id=\"h9d35e22bc5\">\u003Cstrong>音源の読み込みと再生\u003C/strong>\u003C/h4>\u003Cp>\u003Cstrong>音源の読み込みから波形データの取得までを Web Audio API を使って実装\u003C/strong>しました。\u003Cbr>HTMLはとてもシンプルで、\u003Ccode>&lt;audio&gt;\u003C/code>タグを使ってローカルの音源を読み込んでいます。\u003Cbr>\u003Cbr>もうひとつの方法として、JavaScript の \u003Ccode>Audio()\u003C/code> コンストラクタを使うやり方もあります。\u003Cbr>こちらは HTML に要素を追加せずに音源を再生できる上、必要なタイミングで読み込み・再生ができるため、効果音など再生タイミングを細かく制御したいケースに向いていそうです。また、\u003Cstrong>再生後はガベージコレクションの対象\u003C/strong>になるため、\u003Cstrong>メモリ効率が良いという利点\u003C/strong>があります。\u003C/p>\u003Cp>ただし、再生のたびにクローンが必要など少し癖があるため、今回はよりシンプルに扱える \u003Ccode>&lt;audio&gt;\u003C/code> タグを選びました。\u003C/p>\u003Ch4 id=\"hd9cbfd184c\">\u003Cstrong>音の解析構成\u003C/strong>\u003C/h4>\u003Cp>音の解析には \u003Ccode>AnalyserNode\u003C/code> を使用し、音量調整用に \u003Ccode>GainNode\u003C/code> を追加。各ノードは以下のように接続しています。\u003C/p>\u003Cpre>\u003Ccode>audio（音源）\n   ↓\nAnalyserNode（解析）\n   ↓\nGainNode（音量調整）\n   ↓\ndestination（出力）\u003C/code>\u003C/pre>\u003Cp>実際のコードはこちらです。\u003C/p>\u003Cpre>\u003Ccode class=\"language-typescript\">if (!this.analyserNode) {\n  const audioContext = new AudioContext();\n\n  // analyser 生成\n  this.analyserNode = audioContext.createAnalyser();\n\n  // 符号なし long 値で、周波数領域データを取得するために高速フーリエ変換 (FFT) を行う際に使用するウィンドウサイズをサンプル数で表します\n  // ※符号なしlong値とは、負の値ではなく、0以上の整数のみを表現するlong型のデータ型です。通常、32ビットまたは64ビットの長さを持ちます\n  // https://developer.mozilla.org/ja/docs/Web/API/AnalyserNode/fftSize\n  this.analyserNode.fftSize = FFT_SIZE;\n\n  // 最後の分析フレームとの平均化定数を表す double 値です。\n  // これは基本的に、現在のバッファーと AnalyserNode が処理した最後のバッファーとの間の平均であり、\n  // 結果として、よりスムーズな時間による値の変化の集合になります\n  // 0 から 1 までの範囲で、1に近づくほど遅い\n  // https://developer.mozilla.org/ja/docs/Web/API/AnalyserNode/smoothingTimeConstant\n  this.analyserNode.smoothingTimeConstant = 0.5;\n\n  // volume control\n  this.volumeControl = audioContext.createGain();\n  this.volumeControl.gain.value = 0.5;\n\n  // gainNode を出力に接続\n  this.volumeControl.connect(audioContext.destination);\n\n  // analyserNode を gainNode に接続\n  this.analyserNode.connect(this.volumeControl);\n\n  if (this.audio) {\n    this.source = audioContext.createMediaElementSource(this.audio.getElement()!);\n    // source を analyserNode に接続\n    this.source.connect(this.analyserNode);\n  }\n}\n\u003C/code>\u003C/pre>\u003Cp>この接続が正しくないと、音が鳴らなかったり、解析がうまくいかなかったりするので注意が必要です。\u003C/p>\u003Cp>音の解析には \u003Ca href=\"https://www.nti-audio.com/ja/%E3%82%B5%E3%83%9D%E3%83%BC%E3%83%88/%E6%B8%AC%E5%AE%9A%E3%83%8E%E3%82%A6%E3%83%8F%E3%82%A6/%E9%AB%98%E9%80%9F%E3%83%95%E3%83%BC%E3%83%AA%E3%82%A8%E5%A4%89%E6%8F%9B\" target=\"_blank\" rel=\"noopener noreferrer\">\u003Cstrong>FFT(高速フーリエ変換)\u003C/strong>\u003C/a> という手法が使われています。\u003Cbr>これは\u003Cstrong>音を周波数ごとのデータに分解する処理\u003C/strong>で、Web Audio API では AnalyserNode が自動的にこの処理を行ってくれます。\u003C/p>\u003Cp>\u003Ccode>AnalyserNode\u003C/code> では、解析サイズ(2の累乗、今回は1024)や滑らかさの度合い(\u003Ccode>smoothingTimeConstant\u003C/code>：今回はデフォルト値の 0.8)を設定しました。\u003Cstrong>滑らかさを高くすると波形がなだらかに変化し、低くすると急激に変化\u003C/strong>します。 \u003C/p>\u003Cp>周波数データの値は、\u003Cstrong>左から右にかけて低音から高音の順\u003C/strong>に並んでいます。\u003Cbr>こちらの音源はリズム中心だったため、左側が強く反応しました。\u003C/p>\u003Cdiv style=\"left: 0; width: 100%; height: 0; position: relative; padding-bottom: 56.25%;\">\u003Ciframe src=\"https://www.youtube.com/embed/QdndLaguVyU?rel=0&amp;cc_load_policy=1\" style=\"top: 0; left: 0; width: 100%; height: 100%; position: absolute; border: 0;\" allowfullscreen scrolling=\"no\" allow=\"accelerometer *; clipboard-write *; encrypted-media *; gyroscope *; picture-in-picture *; web-share *;\">\u003C/iframe>\u003C/div>\u003Cpre>\u003Ccode class=\"language-typescript\">this.analyserNode = this.analyser.getAnalyserNode();\nif (!this.analyserNode) return;\n\nthis.bufferLength = this.analyser.getBufferLength();\n\n// 8 ビット符号なし整数値の配列\nthis.frequencyData = new Uint8Array(this.bufferLength);\n\n// 渡された Uint8Array （符号なしバイト配列）に現在の周波数データをコピーします\n// 周波数データは、 0 から 255 まで の範囲の整数で構成されます\nthis.analyserNode.getByteFrequencyData(this.frequencyData);\n\nif (!this.analyserNode) return;\n\nif (this.frequencyData &amp;&amp; this.elementList.length &gt; 0) {\n  const baseAngle = 360 / this.bufferLength;\n  this.frequencyData.forEach((data, i) =&gt; {\n    // 値は256段階で取得できるので正規化して 0.0 〜 1.0 の値にする\n    const scale = data / 256;\n    const bar = this.elementList[i];\n    if (bar) {\n      const angle = (baseAngle * i) % 360;\n      bar.style.scale = `1 ${scale}`;\n      bar.style.backgroundColor = `hsl(${angle} 100% 50%)`;\n    }\n  });\n}\n\u003C/code>\u003C/pre>\u003Cp>また、アニメーションを音に合わせて動かす場合、「\u003Cstrong>どの帯域を使うと気持ちいい動きになるか\u003C/strong>」\u003Cstrong>は感覚的\u003C/strong>な部分も大きく、音源によって反応する帯域も異なります。\u003C/p>\u003Cp>例えば\u003Cstrong>音階の違い(ドレミファソラシド)を再生しても、意外と反応の差は小さく\u003C/strong>、単純に「高い音＝右側が反応する」とは限りませんでした。\u003C/p>\u003Cdiv style=\"left: 0; width: 100%; height: 0; position: relative; padding-bottom: 56.25%;\">\u003Ciframe src=\"https://www.youtube.com/embed/Uk7YBIkiRPE?rel=0&amp;cc_load_policy=1\" style=\"top: 0; left: 0; width: 100%; height: 100%; position: absolute; border: 0;\" allowfullscreen scrolling=\"no\" allow=\"accelerometer *; clipboard-write *; encrypted-media *; gyroscope *; picture-in-picture *; web-share *;\">\u003C/iframe>\u003C/div>\u003Ch3 id=\"h1cc71e5f64\">ビジュアライザーのアレンジ例\u003C/h3>\u003Cp>先程のような波形描画は、\u003Ccode>AnalyserNode\u003C/code> が取得できれば比較的簡単に実装できます。\u003Cbr>同じ音のデータを使って、他にどんな表現ができるかいくつか試してみました。\u003C/p>\u003Cp>\u003C/p>\u003Cp>以下の動画では、\u003Cstrong>上から順に3つのアレンジパターン\u003C/strong>を再生しています。\u003C/p>\u003Cdiv style=\"left: 0; width: 100%; height: 0; position: relative; padding-bottom: 56.25%;\">\u003Ciframe src=\"https://www.youtube.com/embed/j3EKi_Vem1g?rel=0&amp;cc_load_policy=1\" style=\"top: 0; left: 0; width: 100%; height: 100%; position: absolute; border: 0;\" allowfullscreen scrolling=\"no\" allow=\"accelerometer *; clipboard-write *; encrypted-media *; gyroscope *; picture-in-picture *; web-share *;\">\u003C/iframe>\u003C/div>\u003Col>\u003Cli>\u003Cstrong>基本の波形描画\u003C/strong>\u003Cbr>横に伸びるバーで音の強弱を表現したもの。\u003C/li>\u003Cli>\u003Cstrong>ブレンド＆ぼかしによる演出\u003C/strong>\u003Cbr>CSS のブレンドモードやぼかし効果を使って、視覚的にカッコよく見えるように工夫したアレンジ。\u003C/li>\u003Cli>\u003Cstrong>円形での音の分布可視化\u003C/strong>\u003Cbr>低周波を外側、高周波を内側に配置し、色でもグラデーションをかけた表現。\u003C/li>\u003C/ol>\u003Cp>※ いずれも DOM のみで描画しています。\u003Cbr>現状、\u003Cstrong>約2000要素の DOM\u003C/strong> が動いていますが、実用的には canvas を使った方が滑らかかつ自由度の高い描画が可能です。\u003C/p>\u003Cp>上のアレンジをベースに、\u003Cstrong>もっと映像っぽくカッコよく見えるように調整したバージョン\u003C/strong>も作ってみました。\u003C/p>\u003Col>\u003Cli>\u003Cstrong>反転パターン\u003C/strong>\u003Cbr>最初に作った波形を縦に並べ、上下左右に反転させて描画したものです。\u003Cbr>\u003Cbr>\u003Cdiv style=\"left: 0; width: 100%; height: 0; position: relative; padding-bottom: 56.25%;\">\u003Ciframe src=\"https://www.youtube.com/embed/yFdkYY4ZUNw?rel=0&amp;cc_load_policy=1\" style=\"top: 0; left: 0; width: 100%; height: 100%; position: absolute; border: 0;\" allowfullscreen scrolling=\"no\" allow=\"accelerometer *; clipboard-write *; encrypted-media *; gyroscope *; picture-in-picture *; web-share *;\">\u003C/iframe>\u003C/div>\u003Cp>\u003Cbr>\u003C/p>\u003C/li>\u003Cli>\u003Cstrong>複製パターン\u003C/strong>\u003Cbr>先ほどの 2 つ目のアレンジを左右反転して複製したものです。\u003Cbr>\u003Cbr>\u003Cdiv style=\"left: 0; width: 100%; height: 0; position: relative; padding-bottom: 56.25%;\">\u003Ciframe src=\"https://www.youtube.com/embed/9yo3vf_BXBg?rel=0&amp;cc_load_policy=1\" style=\"top: 0; left: 0; width: 100%; height: 100%; position: absolute; border: 0;\" allowfullscreen scrolling=\"no\" allow=\"accelerometer *; clipboard-write *; encrypted-media *; gyroscope *; picture-in-picture *; web-share *;\">\u003C/iframe>\u003C/div>\u003Cp>\u003Cbr>\u003C/p>\u003C/li>\u003Cli>\u003Cstrong>ブレンドモード重ねパターン\u003C/strong>\u003Cbr>3 つ目のアレンジにブレンドモードを適用し、図形同士の重なりを強調しています。\u003Cbr>この円だけを表示しても何も見えませんが、背面に何か(ここでは1つ目のアレンジなど)を表示させて重ねることで、中央に円が浮かび上がる仕掛けになっています。\u003Cbr>\u003Cbr>\u003Cdiv style=\"left: 0; width: 100%; height: 0; position: relative; padding-bottom: 56.25%;\">\u003Ciframe src=\"https://www.youtube.com/embed/4eGjZJLEMtI?rel=0&amp;cc_load_policy=1\" style=\"top: 0; left: 0; width: 100%; height: 100%; position: absolute; border: 0;\" allowfullscreen scrolling=\"no\" allow=\"accelerometer *; clipboard-write *; encrypted-media *; gyroscope *; picture-in-picture *; web-share *;\">\u003C/iframe>\u003C/div>\u003Cp>\u003Cbr>\u003C/p>\u003C/li>\u003Cli>\u003Cstrong>回転・ボーダー化パターン\u003C/strong>\u003Cbr>円形波形をもとに、背景色をなくしボーダー(線)だけで構成。図形そのものを回転させるアレンジです。\u003Cbr>\u003Cbr>\u003Cdiv style=\"left: 0; width: 100%; height: 0; position: relative; padding-bottom: 56.25%;\">\u003Ciframe src=\"https://www.youtube.com/embed/8ksAD6gE9po?rel=0&amp;cc_load_policy=1\" style=\"top: 0; left: 0; width: 100%; height: 100%; position: absolute; border: 0;\" allowfullscreen scrolling=\"no\" allow=\"accelerometer *; clipboard-write *; encrypted-media *; gyroscope *; picture-in-picture *; web-share *;\">\u003C/iframe>\u003C/div>\u003C/li>\u003C/ol>\u003Cp>そして、これら4つを\u003Cstrong>すべて組み合わせたバージョン\u003C/strong>がこちら。\u003Cbr>重なりや動きに奥行きが生まれ、よりビジュアルとしての完成度が上がりました。\u003C/p>\u003Cdiv style=\"left: 0; width: 100%; height: 0; position: relative; padding-bottom: 56.25%;\">\u003Ciframe src=\"https://www.youtube.com/embed/L5Zn9kPweQQ?rel=0&amp;cc_load_policy=1\" style=\"top: 0; left: 0; width: 100%; height: 100%; position: absolute; border: 0;\" allowfullscreen scrolling=\"no\" allow=\"accelerometer *; clipboard-write *; encrypted-media *; gyroscope *; picture-in-picture *; web-share *;\">\u003C/iframe>\u003C/div>\u003Cp>また、AnalyserNode の \u003Ccode>smoothingTimeConstant\u003C/code> を調整することで、\u003Cstrong>アニメーションの印象\u003C/strong>も大きく変わります。\u003Cbr>例えば値を 0 に近づけると反応が鋭くなり、クラブで流れるような激しめのチカチカした演出に。\u003Cbr>逆に 1 に近づけると動きがなだらかになり、滑らかでスムーズな映像表現になります。\u003C/p>\u003Ch2 id=\"ha214098e44\">まとめ\u003C/h2>\u003Cp>データ自体は簡単に取得できるため、\u003Cstrong>WebGL で描画したり、フェーダーやシーケンサー的な UI を組み合わせたりすれば、見た目にも凄そうなものが意外と手軽に\u003C/strong>作れてしまいそうです。\u003C/p>\u003Cp>最近では、Webサイト上で音を使う機会は減ってきている印象もありますが、だからこそ、\u003Cstrong>音に合わせて何かが動くような演出\u003C/strong>があると、逆に新鮮に映るかもしれません。\u003C/p>\u003Cp>実際に、以前担当した案件では音楽に合わせて画像の動きを変化させる実装を行ったのですが、そのときは知識も浅く、波形の一部しか使っていませんでした。\u003Cbr>この度あらためて試してみて、\u003Cstrong>AnalyserNode を使えば遥かに多彩な表現が可能だということを実感\u003C/strong>したので、今後、また音を扱うような場面があれば、ぜひこうした表現にも挑戦してみたいと思います。\u003C/p>\u003Cp>このような音とビジュアルを組み合わせた表現に興味がある方は、\u003Ca href=\"https://musiclab.chromeexperiments.com/Experiments\" target=\"_blank\" rel=\"noopener noreferrer nofollow\">\u003Cstrong>Chrome Music Lab\u003C/strong>\u003C/a> もぜひチェックしてみてください。手軽に試せる実験的なコンテンツがいろいろ用意されています。\u003C/p>\u003Cp>\u003Cstrong>「こういう表現をWebでやってみたい」「音に合わせて動く演出を作ってみたい」といったご相談があれば、ぜひお気軽に\u003C/strong>\u003Ca href=\"https://www.s2factory.co.jp/contact\" target=\"_blank\" rel=\"noopener noreferrer\">\u003Cstrong>お問い合わせ\u003C/strong>\u003C/a>\u003Cstrong>ください。\u003C/strong>\u003Cbr>\u003Cbr>\u003C/p>\u003Cdiv class=\"iframely-embed\">\u003Cdiv class=\"iframely-responsive\" style=\"height: 140px; padding-bottom: 0;\">\u003Ca href=\"https://www.s2factory.co.jp/\" data-iframely-url=\"https://cdn.iframe.ly/api/iframe?card=small&amp;url=https%3A%2F%2Fwww.s2factory.co.jp%2Fcontact&amp;key=c271a3ec77ff4aa44d5948170dd74161\">\u003C/a>\u003C/div>\u003C/div>\u003Cscript async src=\"https://cdn.iframe.ly/embed.js\" charset=\"utf-8\">\u003C/script>",{"url":73,"height":74,"width":75},"https://images.microcms-assets.io/assets/0eb919b93c74437dbd287ab903d9ea2c/61730b50f2624b8d8cc1444eeb6163ed/%E3%83%93%E3%82%B8%E3%83%A5%E3%82%A2%E3%83%A9%E3%82%A4%E3%82%B6%E3%83%BC.png",1332,2224,[77,79,81,82],{"id":78},"music",{"id":80},"hobby",{"id":27},{"id":16},[],[],{"id":86,"createdAt":87,"updatedAt":88,"publishedAt":89,"revisedAt":90,"title":91,"content2":92,"eyecatch":93,"tag":97,"heading_list":36,"recruit":20,"is_html":20,"related_articles":102,"recommended_articles":103,"is_migration":20},"svg-animation","2023-08-17T09:14:41.273Z","2023-08-23T00:32:13.571Z","2022-05-31T09:00:00.000Z","2023-08-18T07:37:22.060Z","SVGでうねうね動く円のアニメーション","\u003Cp>今回は弊社の森崎が\u003Cstrong>「有機的な円のアニメーション」\u003C/strong>の実装について話しました。\u003C/p>\u003Chr>\u003Cp>\u003Cbr>たまたま 2 案件続いて円のアニメーションを有機的に動かしたいという要望が出たので、実装方法を考えてみました。\u003Cbr>\u003C/p>\u003Ch2 id=\"h155045379c\">方法を考える\u003C/h2>\u003Cp>最終目標は、\u003Cstrong>SVG でウネウネする円を描画\u003C/strong>することです。\u003Cbr>ということで方法をいくつか考えて、それぞれのメリットとデメリットを書き出してみます。\u003Cbr>\u003Cbr>\u003Cbr>\u003Cstrong>gifアニメ、APNG\u003C/strong>\u003C/p>\u003Cul>\u003Cli>メリット\u003Cul>\u003Cli>実装は \u003Ccode>img\u003C/code> で挿入するだけなので楽。\u003C/li>\u003C/ul>\u003C/li>\u003Cli>デメリット\u003Cul>\u003Cli>画像を作成するのが大変。\u003C/li>\u003Cli>調整／修正しづらい。\u003C/li>\u003Cli>決まった動きしかできないため単調になる。\u003C/li>\u003C/ul>\u003C/li>\u003C/ul>\u003Cp>\u003Cbr>\u003Cstrong>canvas\u003C/strong>\u003C/p>\u003Cul>\u003Cli>メリット\u003Cul>\u003Cli>パフォーマンスに優れる。\u003C/li>\u003C/ul>\u003C/li>\u003Cli>デメリット\u003Cul>\u003Cli>\u003Cstrong>ラスター形式\u003C/strong>のため描画が荒くなってしまう可能性がある。\u003C/li>\u003Cli>拡大縮小への対応が大変。\u003C/li>\u003C/ul>\u003C/li>\u003C/ul>\u003Cp>\u003Cbr>\u003Cstrong>SVG\u003C/strong>\u003C/p>\u003Cul>\u003Cli>メリット\u003Cul>\u003Cli>\u003Cstrong>ベクター形式\u003C/strong>のため描画がキレイ。\u003C/li>\u003Cli>パフォーマンスも悪くない。\u003C/li>\u003C/ul>\u003C/li>\u003Cli>デメリット\u003Cul>\u003Cli>実装が少し大変。\u003C/li>\u003C/ul>\u003C/li>\u003C/ul>\u003Cp>\u003Cbr>案件の内容的に、\u003Cstrong>単色でぼかしなどのエフェクトも使用しないシンプルなアニメーション\u003C/strong>だったので、 今回は \u003Cstrong>SVG を採用\u003C/strong>することにしました。\u003Cbr>\u003Cbr>\u003C/p>\u003Ch2 id=\"h3906b8a178\">SVG とは\u003C/h2>\u003Cp>\u003Cstrong>SVG は Scalable Vector Graphics の略\u003C/strong>です。直訳すると「拡大縮小可能な\u003Ca href=\"https://e-words.jp/w/%E3%83%99%E3%82%AF%E3%82%BF%E3%83%BC%E7%94%BB%E5%83%8F.html\">ベクター画像\u003C/a>」。\u003Cbr>1999年に \u003Ca href=\"https://www.w3.org/\">W3C\u003C/a> によって開発された \u003Cstrong>xml に基づくマークアップ言語\u003C/strong>です。テキストなので script などから操作が可能で、またテキストエディタで編集できます。\u003Cbr>\u003Cbr>ベクター画像とは、\u003C/p>\u003Cblockquote>\u003Cp>\u003Cstrong>画像データの表現形式の一つで、画像を図形を表す数値情報の集合として表現したもの。サイズや解像度によらず同じ品質の出力結果を得ることができる。\u003C/strong>\u003C/p>\u003Cp>\u003Cbr>\u003C/p>\u003Cp>\u003Cstrong>出典：\u003C/strong>\u003Ca href=\"https://e-words.jp/w/%E3%83%99%E3%82%AF%E3%82%BF%E3%83%BC%E7%94%BB%E5%83%8F.html\" target=\"_blank\" rel=\"noopener noreferrer nofollow\">\u003Cstrong>IT用語辞典 e-Words「ベクター画像」\u003C/strong>\u003C/a>\u003C/p>\u003C/blockquote>\u003Cp>いわゆる\u003Cstrong>写真などはラスター画像\u003C/strong>と呼ばれておりベクター画像とは対極のもので、\u003Cstrong>拡大・縮小するとピクセルの荒さが目立ったりします\u003C/strong>が、\u003Cstrong>ベクター画像は全てテキスト情報で計算されるため綺麗にレンダリングされます。\u003C/strong>\u003Cbr>\u003Cbr>なので、レスポンシブなディスプレイが使用されるようになって以降ウェブ制作で使われることが増えました。\u003Cbr>\u003Cbr>基本図形にはこれらがあって、\u003C/p>\u003Cul>\u003Cli>\u003Ca href=\"https://developer.mozilla.org/ja/docs/Web/SVG/Element/circle\" target=\"_blank\" rel=\"noopener noreferrer nofollow\">\u003Cstrong>circle\u003C/strong>\u003C/a>\u003Cstrong>（円）\u003C/strong>\u003C/li>\u003Cli>\u003Ca href=\"https://developer.mozilla.org/ja/docs/Web/SVG/Element/ellipse\" target=\"_blank\" rel=\"noopener noreferrer nofollow\">\u003Cstrong>ellipse\u003C/strong>\u003C/a>\u003Cstrong>（楕円）\u003C/strong>\u003C/li>\u003Cli>\u003Ca href=\"https://developer.mozilla.org/ja/docs/Web/SVG/Element/line\" target=\"_blank\" rel=\"noopener noreferrer nofollow\">\u003Cstrong>line\u003C/strong>\u003C/a>\u003Cstrong>（線）\u003C/strong>\u003C/li>\u003Cli>\u003Ca href=\"https://developer.mozilla.org/ja/docs/Web/SVG/Element/path\" target=\"_blank\" rel=\"noopener noreferrer nofollow\">\u003Cstrong>path\u003C/strong>\u003C/a>\u003Cstrong>（パス）\u003C/strong>\u003C/li>\u003Cli>\u003Ca href=\"https://developer.mozilla.org/ja/docs/Web/SVG/Element/polygon\" target=\"_blank\" rel=\"noopener noreferrer nofollow\">\u003Cstrong>polygon\u003C/strong>\u003C/a>\u003Cstrong>（多角形）\u003C/strong>\u003C/li>\u003Cli>\u003Ca href=\"https://developer.mozilla.org/ja/docs/Web/SVG/Element/polyline\" target=\"_blank\" rel=\"noopener noreferrer nofollow\">\u003Cstrong>polyline\u003C/strong>\u003C/a>\u003Cstrong>（折れ線）\u003C/strong>\u003C/li>\u003Cli>\u003Ca href=\"https://developer.mozilla.org/ja/docs/Web/SVG/Element/rect\" target=\"_blank\" rel=\"noopener noreferrer nofollow\">\u003Cstrong>rect\u003C/strong>\u003C/a>\u003Cstrong>（矩形）\u003C/strong>\u003C/li>\u003C/ul>\u003Cp>それぞれ専用のタグを持っています。\u003Cbr>中でも \u003Cstrong>path\u003C/strong> は基本図形の中で最も汎用的な要素で、それだけで\u003Cstrong>全ての基本図形の作成が可能\u003C/strong>です。\u003C/p>\u003Cpre>\u003Ccode>&lt;path d=&quot;M 10 10 H 90 V 90 H 10 L 10 10 Z&quot;/&gt;\u003C/code>\u003C/pre>\u003Cp>のように、\u003Cstrong>コマンド（命令）とパラメータ（座標）を指定して描画\u003C/strong>させます。\u003Cbr>\u003Cbr>\u003C/p>\u003Ch3 id=\"h2af693514d\">コマンド\u003C/h3>\u003Cp>コマンドには大きく分けて\u003Cstrong>直線コマンドと曲線コマンド\u003C/strong>があり、例えば \u003Ccode>moveTo\u003C/code>、\u003Ccode>lineTo\u003C/code> というコマンドを使いたい時には \u003Ccode>M or m\u003C/code>、\u003Ccode>L or l\u003C/code> などの1文字で表します。というのもそのまま \u003Ccode>moveTo\u003C/code> のように書いていくとものすごい長さになってしまうからだそうです。この時、\u003Cstrong>大文字は絶対座標、小文字は相対座標\u003C/strong>を表しています。\u003Cbr>\u003Cbr>\u003Cstrong>直線コマンド\u003C/strong>\u003C/p>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Cth colspan=\"1\" rowspan=\"1\">\u003Cp>\u003Cstrong>コマンド\u003C/strong>\u003C/p>\u003C/th>\u003Cth colspan=\"1\" rowspan=\"1\">\u003Cp>\u003Cstrong>命令\u003C/strong>\u003C/p>\u003C/th>\u003Cth colspan=\"1\" rowspan=\"1\">\u003Cp>\u003Cstrong>意味\u003C/strong>\u003C/p>\u003C/th>\u003C/tr>\u003Ctr>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>\u003Ccode>M\u003C/code> x y\u003Cbr>\u003Ccode>m\u003C/code> dx dy\u003C/p>\u003C/td>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>\u003Cstrong>moveto\u003C/strong>\u003C/p>\u003C/td>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>\u003Cstrong>指定した座標に移動。\u003C/strong>\u003C/p>\u003C/td>\u003C/tr>\u003Ctr>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>\u003Ccode>L\u003C/code> x y\u003Cbr>\u003Ccode>l\u003C/code> dx dy\u003C/p>\u003C/td>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>\u003Cstrong>lineto\u003C/strong>\u003C/p>\u003C/td>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>\u003Cstrong>指定した座標まで線を描画。\u003C/strong>\u003C/p>\u003C/td>\u003C/tr>\u003Ctr>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>\u003Ccode>H\u003C/code> x\u003Cbr>\u003Ccode>h\u003C/code> dx\u003C/p>\u003C/td>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>\u003Cstrong>horizontal lineto\u003C/strong>\u003C/p>\u003C/td>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>\u003Cstrong>指定したx座標まで線を描画。\u003C/strong>\u003C/p>\u003C/td>\u003C/tr>\u003Ctr>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>\u003Ccode>V\u003C/code> y\u003Cbr>\u003Ccode>v\u003C/code> dy\u003C/p>\u003C/td>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>\u003Cstrong>vertical lineto\u003C/strong>\u003C/p>\u003C/td>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>\u003Cstrong>指定したy座標まで線を描画。\u003C/strong>\u003C/p>\u003C/td>\u003C/tr>\u003Ctr>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>\u003Ccode>Z\u003C/code>\u003Cbr>\u003Ccode>z\u003C/code>\u003C/p>\u003C/td>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>\u003Cstrong>closepath\u003C/strong>\u003C/p>\u003C/td>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>\u003Cstrong>始点に向かってパスを閉じる。\u003Cbr>パラメータがないので大文字でも小文字でも意味は同じ。\u003C/strong>\u003C/p>\u003C/td>\u003C/tr>\u003C/tbody>\u003C/table>\u003Cp>\u003Cstrong>曲線コマンド\u003C/strong>\u003C/p>\u003Ctable>\u003Ctbody>\u003Ctr>\u003Cth colspan=\"1\" rowspan=\"1\">\u003Cp>\u003Cstrong>コマンド\u003C/strong>\u003C/p>\u003C/th>\u003Cth colspan=\"1\" rowspan=\"1\">\u003Cp>\u003Cstrong>名前\u003C/strong>\u003C/p>\u003C/th>\u003Cth colspan=\"1\" rowspan=\"1\">\u003Cp>\u003Cstrong>意味\u003C/strong>\u003C/p>\u003C/th>\u003C/tr>\u003Ctr>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>\u003Ccode>C\u003C/code> x1 y1, x2 y2, x y\u003Cbr>\u003Ccode>c\u003C/code> dx1 dy1, dx2 dy2, dx dy\u003C/p>\u003C/td>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>\u003Cstrong>curveto\u003C/strong>\u003C/p>\u003C/td>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>\u003Ca href=\"https://codepen.io/SitePoint/pen/mdrdpVr\" target=\"_blank\" rel=\"noopener noreferrer nofollow\">\u003Cstrong>三次ベジェ曲線\u003C/strong>\u003C/a>\u003Cstrong>\u003Cbr>現在の点から、曲線の始点に対応する第一制御点 (x1, y1) と曲線の終点に対応する第二制御点 (x2, y2) を用いて点 (x, y) へ三次ベジェ曲線を描く。\u003C/strong>\u003C/p>\u003C/td>\u003C/tr>\u003Ctr>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>\u003Ccode>S\u003C/code> x2 y2, x y\u003Cbr>\u003Ccode>s\u003C/code> dx2 dy2, dx dy\u003C/p>\u003C/td>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>\u003Cstrong>shorthand/smooth\u003Cbr>curveto\u003C/strong>\u003C/p>\u003C/td>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>\u003Cstrong>現在の点から点 (x, y) へ三次ベジェ曲線を描く。\u003Cbr>第一制御点は前の命令の第二制御点の現在の点に対する鏡像（点対称）の地点とみなされる（もし前の命令が無いか、あるいは C, c, S, s のいずれでもない場合、第一制御点は現在の点と同一のものとみなされる）。\u003C/strong>\u003C/p>\u003C/td>\u003C/tr>\u003Ctr>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>\u003Ccode>Q\u003C/code> x1 y1, x y\u003Cbr>\u003Ccode>q\u003C/code> dx1 dy1, dx dy\u003C/p>\u003C/td>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>\u003Cstrong>quadratic Bézier curveto\u003C/strong>\u003C/p>\u003C/td>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>\u003Ca href=\"https://blogs.sitepointstatic.com/examples/tech/svg-curves/quadratic-curve.html\" target=\"_blank\" rel=\"noopener noreferrer nofollow\">\u003Cstrong>二次ベジェ曲線\u003C/strong>\u003C/a>\u003Cstrong>\u003Cbr>現在の点から制御点 (x1, y1) を用いて点 (x, y) へ二次ベジェ曲線を描く。\u003C/strong>\u003C/p>\u003C/td>\u003C/tr>\u003Ctr>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>\u003Ccode>T\u003C/code> x y\u003Cbr>\u003Ccode>t\u003C/code> dx dy\u003C/p>\u003C/td>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>\u003Cstrong>Shorthand/smooth\u003Cbr>quadratic Bézier curveto\u003C/strong>\u003C/p>\u003C/td>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>\u003Cstrong>現在の点から点 (x, y) へ二次ベジェ曲線を描く。\u003Cbr>制御点は前の命令の制御点の現在の点に対する鏡像（点対称）の点とみなされる（もし前の命令が無いか、あるいは Q, q, T, t のいずれでもない場合、\u003Cbr>第一制御点は現在の点と同一のものとみなされる）。\u003C/strong>\u003C/p>\u003C/td>\u003C/tr>\u003Ctr>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>\u003Ccode>A\u003C/code> rx ry x-axis-rotation large-arc-flag sweep-flag x y\u003Cbr>\u003Ccode>a\u003C/code> rx ry x-axis-rotation large-arc-flag sweep-flag dx dy\u003C/p>\u003C/td>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>\u003Cstrong>elliptical arc\u003C/strong>\u003C/p>\u003C/td>\u003Ctd colspan=\"1\" rowspan=\"1\">\u003Cp>\u003Cstrong>現在の点から点 (x, y) へ楕円形の弧を描く。\u003Cbr>詳しくは\u003C/strong>\u003Ca href=\"https://triple-underscore.github.io/SVG11/paths.html#PathDataEllipticalArcCommands\" target=\"_blank\" rel=\"noopener noreferrer nofollow\">\u003Cstrong>こちら\u003C/strong>\u003C/a>\u003Cstrong>。\u003C/strong>\u003C/p>\u003C/td>\u003C/tr>\u003C/tbody>\u003C/table>\u003Ch3 id=\"hd823c3a8a7\">\u003Cstrong>\u003Cbr>\u003C/strong>フロントエンドでの SVG の扱い方\u003C/h3>\u003Cp>フロントエンドでは \u003Ccode>img\u003C/code> タグや \u003Ccode>svg\u003C/code> タグを使って描画させます。\u003Cbr>\u003Cbr>\u003Ccode>img\u003C/code> タグ\u003C/p>\u003Cul>\u003Cli>メリット\u003Cul>\u003Cli>\u003Ccode>src\u003C/code> に パスを指定するだけなので簡単。\u003C/li>\u003C/ul>\u003C/li>\u003Cli>デメリット\u003Cul>\u003Cli>基本的に扱いは\u003Ccode>画像\u003C/code>となるので、細かな操作は出来ない。\u003C/li>\u003C/ul>\u003C/li>\u003C/ul>\u003Cp>\u003Ccode>svg\u003C/code> タグ\u003C/p>\u003Cul>\u003Cli>メリット\u003Cul>\u003Cli>テキストデータとして扱えるので、script で細かな操作が可能。\u003C/li>\u003Cli>CSS による装飾が可能。\u003C/li>\u003C/ul>\u003C/li>\u003Cli>デメリット\u003Cul>\u003Cli>書くことが増えるためコードの見通しが悪くなる。\u003C/li>\u003C/ul>\u003C/li>\u003C/ul>\u003Cp>\u003Cbr>\u003Ccode>object\u003C/code> タグを使っても実装できますが、使うメリットはなさそうな印象。\u003Cbr>\u003Cbr>\u003Cstrong>ライブラリが充実\u003C/strong>しているのでそれを使った方が実装も簡単になるし、表現の自由度も上がりそうでした。さらにレガシーブラウザへの対応も可能になるため使っておいて損はないように思います。\u003Cbr>\u003Cbr>代表的なところだと、\u003C/p>\u003Cul>\u003Cli>\u003Ca href=\"http://dmitrybaranovskiy.github.io/raphael/\">\u003Cstrong>Raphaël.js\u003C/strong>\u003C/a>\u003Cul>\u003Cli>昔からあるパイオニア的ライブラリ。\u003C/li>\u003Cli>IE 6+/Safari 3.0+/Firefox 3.0+ に対応。\u003C/li>\u003C/ul>\u003C/li>\u003Cli>\u003Ca href=\"http://snapsvg.io/\" target=\"_blank\" rel=\"noopener noreferrer nofollow\">\u003Cstrong>Snap.svg\u003C/strong>\u003C/a>\u003Cul>\u003Cli>\u003Ccode>Adobe\u003C/code> 製。\u003C/li>\u003Cli>\u003Ccode>Raphaël.js\u003C/code> と同じ人が作っている。\u003C/li>\u003Cli>機能豊富。\u003C/li>\u003Cli>チェーンメソッド採用で、\u003Ccode>JQuery\u003C/code> と相性がいい。\u003C/li>\u003Cli>Typescript対応。\u003C/li>\u003Cli>IE 9+/Chrome/Safari/Firefox に対応。\u003C/li>\u003C/ul>\u003C/li>\u003Cli>\u003Ca href=\"https://svgjs.dev/docs/3.0/\" target=\"_blank\" rel=\"noopener noreferrer nofollow\">\u003Cstrong>SVG.js\u003C/strong>\u003C/a>\u003Cul>\u003Cli>軽量かつ高速。\u003C/li>\u003Cli>チェーンメソッド採用。\u003C/li>\u003Cli>プラグインで機能追加可能。\u003C/li>\u003Cli>Typescript対応。\u003C/li>\u003Cli>IE 9+/Chrome 4.0+/Safari 3.2+/Firefox 3.0+ に対応。\u003C/li>\u003C/ul>\u003C/li>\u003Cli>\u003Ca href=\"http://www.graphicsjs.org/\">\u003Cstrong>GraphicsJS\u003C/strong>\u003C/a>\u003Cul>\u003Cli>比較的新しい。\u003C/li>\u003C/ul>\u003Cul>\u003Cli>AnyChart というデータビジュアライゼーションが得意な会社が作成。\u003C/li>\u003Cli>グラフやチャートなどを作成する機能が豊富。\u003C/li>\u003Cli>仮想DOMに対応。\u003C/li>\u003Cli>IE 6+/Chrome 1.0+/Safari 4.0+/Firefox 2.0+ に対応。\u003C/li>\u003C/ul>\u003C/li>\u003C/ul>\u003Cp>などがあります。\u003Cbr>\u003Cbr>\u003C/p>\u003Ch2 id=\"h465f8a8db1\">実際にやってみる\u003C/h2>\u003Cp>\u003Cstrong>目標\u003C/strong>：\u003Cstrong>円周を動かしたい\u003C/strong>\u003Cbr>\u003Cbr>SVGの \u003Ccode>circle\u003C/code> タグでは円の描画はできても、円弧のアニメーションは作れないので \u003Ccode>path\u003C/code> タグを使う必要があります。\u003Cbr>また、\u003Ccode>path\u003C/code> を使うなら二次ベジェ曲線か三次ベジェ曲線どちらを使うのか決めないといけませんが、三次ベジェの方がちょっと複雑になりそうだったので、今回は二次ベジェを使うことにしました。\u003Cbr>\u003Cbr>\u003C/p>\u003Ch3 id=\"h353c278d8b\">STEP-1\u003C/h3>\u003Cp>まずポイントを作成します。\u003Cbr>\u003Cstrong>大体8等分ぐらいでほぼ真円と区別がつかないぐらいの近似曲線が描画できる\u003C/strong>そうです。\u003Cbr>\u003Cbr>ポイントは、\u003C/p>\u003Cul>\u003Cli>\u003Cstrong>コントロールポイント（制御点）\u003C/strong>\u003C/li>\u003Cli>\u003Cstrong>アンカーポイント（始点、終点）\u003C/strong>\u003C/li>\u003C/ul>\u003Cp>の 2 パターンを作ります。\u003Cbr>それらを結びつけながら円弧を描画していきます。\u003Cbr>ライブラリは SVG.js 使用しました。\u003Cbr>\u003Cbr>\u003Cbr>右上のパネル 1 つ目のチェックボックスはコントロールポイントです。\u003Cbr>2 つ目をクリックするとそれらが線で結ばれます。\u003Cbr>3 つ目がアンカーポイント。線と線の中点を取っています。アンカーポイント 2 つと、その間にあるコントロールポイントで円弧を描いています。\u003Cbr>\u003Cbr>\u003C/p>\u003Ch3 id=\"hcb28eea180\">STEP-2\u003C/h3>\u003Cp>準備ができたので、いよいよアニメーションを実装します。\u003Cbr>やり方としては、\u003C/p>\u003Col>\u003Cli>\u003Cstrong>コントロールポイント(青い点)を円周に対して直角に上下動させる\u003C/strong>\u003C/li>\u003Cli>\u003Cstrong>隣り合うコントロールポイントの中点を取得して、アンカーポイント(赤い点)の位置を計算し直す\u003C/strong>\u003C/li>\u003Cli>\u003Cstrong>コントロールポイントとアンカーポイントを元に円弧を再描画する\u003C/strong>\u003C/li>\u003C/ol>\u003Cp>こうすることで、一つずつの線が動いているようなアニメーションを実装しました。\u003Cbr>パネルを操作することで楕円形にしたり、座標を変更して部分的に円が見えるようにしたり、色々な設定が可能です。\u003Cbr>\u003Cbr>\u003Cbr>ポイントを増やすことで複雑な動きをさせることも可能ですが、円のアニメーションに見えなくなってしまうので 8~16 ぐらいがちょうど良さそうに思いました。\u003Cbr>\u003Cbr>今回は SVG と SVG.js を使って実装しましたが、他に何か良い方法があればぜひ教えてください。\u003C/p>",{"url":94,"height":95,"width":96},"https://images.microcms-assets.io/assets/0eb919b93c74437dbd287ab903d9ea2c/685a881b637f4bda9698881e5a46c065/2022-05-8d9d03aff7e3c5fb1462b35f2933eaec.png",1103,1605,[98,99,100,101],{"id":31},{"id":22},{"id":16},{"id":27},[],[],{"id":105,"createdAt":106,"updatedAt":107,"publishedAt":106,"revisedAt":107,"title":108,"eyecatch":109,"tag":113,"heading_list":20,"recruit":20,"is_html":36,"html_contents":117,"related_articles":118,"recommended_articles":119,"is_migration":36,"migration_date":120},"vite","2023-06-02T08:49:39.185Z","2023-08-07T04:44:32.239Z","Viteについて",{"url":110,"height":111,"width":112},"https://images.microcms-assets.io/assets/0eb919b93c74437dbd287ab903d9ea2c/e638afb22c954d8580b59c9ed1209319/2022-03-1a391e56e6b21f31e74b7f2faae771a3.png",574,990,[114,115,116],{"id":31},{"id":16},{"id":27},"今回は弊社の小泉が\u003Cstrong>「Vite 」\u003C/strong>について話しました。\n\n\u003Chr class=\"wp-block-separator\"/>\n\n\u003Cbr>今回のお茶会は「\u003Cstrong>\u003Ca rel=\"noreferrer noopener\" href=\"https://ja.vitejs.dev/\" target=\"_blank\">Vite\u003C/a>\u003C/strong>」というフロントエンドのビルドツールについて話します。\u003Cbr>\n\n\u003Ch2>Viteとは\u003C/h2>\n\nVite(ヴィート＝フランス語で「速い」の意味)は\u003Cstrong>2020年に発表された新しいフロントエンドのビルドツール\u003C/strong>。\u003Cbr>\u003Cbr>\u003Cstrong>\u003Ca rel=\"noreferrer noopener\" href=\"https://jp.vuejs.org/index.html\" target=\"_blank\">Vue.js\u003C/a> の開発者である \u003Cem>\u003Ca rel=\"noreferrer noopener\" href=\"https://twitter.com/youyuxi\" target=\"_blank\">Evan You\u003C/a>\u003C/em> 氏によって開発された\u003C/strong>ため、Vue.js のツールだと誤解されることがありますが、プレーンな JavaScript (バニラJS)から Vue.js はもちろん、\u003Ca rel=\"noreferrer noopener\" href=\"https://ja.reactjs.org/\" target=\"_blank\">React\u003C/a>・\u003Ca rel=\"noreferrer noopener\" href=\"https://svelte.jp/\" target=\"_blank\">Svelte\u003C/a> といった流行のフレームワークまで、様々な環境で利用できる\u003Cstrong>汎用的なツール\u003C/strong>です。\u003Cbr>\u003Cbr>位置付けとしては \u003Ca rel=\"noreferrer noopener\" href=\"https://webpack.js.org/\" target=\"_blank\">webpack\u003C/a> のようなバンドラーと呼ばれるものに近い存在になります。\u003Cbr>寝る前にアイデアを思いついて一晩で作ってしまったらしいです。\u003Cbr>\u003Cbr>\n\n\u003Cblockquote class=\"twitter-tweet\">\u003Cp lang=\"en\" dir=\"ltr\">As I was going to bed, I had an idea about a no-bundler dev setup (using native browser ES imports), but with support for Vue SFCs **with hot reload**. Now it's almost 6AM and I have PoC working. The hot reload is so fast it's near instant.\u003C/p>— Evan You (@youyuxi) \u003Ca href=\"https://twitter.com/youyuxi/status/1252173663199277058?ref_src=twsrc%5Etfw\">April 20, 2020\u003C/a>\u003C/blockquote>\n\n\u003Cscript async=\"\" src=\"https://platform.twitter.com/widgets.js\" charset=\"utf-8\">\u003C/script>\n\n\u003Ch2>特徴\u003C/h2>\n\n\u003Col>\u003Cli>\u003Cstrong>モダンブラウザが備える仕組み(ES modules や Dynamic import)を利用して高速な起動 / 更新ができる\u003C/strong>\u003C/li>\u003Cli>\u003Cstrong>TypeScript や Vue / React などのライブラリが設定なしですぐに使える\u003C/strong>\u003C/li>\u003Cli>\u003Cstrong>IE を含めたプロダクションビルドも可能\u003C/strong>\u003C/li>\u003C/ol>\n\n\u003Cbr>といった点が大きな特徴になります。\u003Cbr>\u003Cbr>フロントの実装をやったことのある人なら覚えがあるかもしれませんが、これまでは webpack などを用いてモジュールの依存関係を解消した上で一つの js ファイルにデータを \u003Ca href=\"https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Statements/import\">import\u003C/a>  して再構築し、フロントエンド用のファイルとして開発環境に出力していました。\u003Cbr>その際、アプリケーション全体をクロールしてバンドルする必要があるので、規模が大きくなるほど時間がかかってしまう難点がありました。\u003Cbr>Vite はアプリケーションのモジュールを\u003Cstrong>「依存関係」と「ソースコード」に分けることでこの問題を解決\u003C/strong>したそうです。\u003Cbr>\u003Cbr>Vite では、\u003Cstrong>\u003Ca rel=\"noreferrer noopener\" href=\"https://esbuild.github.io/\" target=\"_blank\">esbuild\u003C/a>&nbsp;を使用して依存関係の事前バンドル\u003C/strong>を行います。\u003Cbr>公式によれば esbuild は \u003Ca rel=\"noreferrer noopener\" href=\"https://go.dev/\" target=\"_blank\">Go 言語\u003C/a>によって開発されているそうで、\u003Cstrong>JavaScript ベースのバンドラーよりも 10 倍から 100 倍高速\u003C/strong>とのことです。\u003Cbr>さらに、\u003Cstrong>ソースコードはネイティブの ES Modules で提供することで、バンドルすることなくそのままブラウザに読み込ませる\u003C/strong>ことができるようになっています。\u003Cbr>\u003Cbr>\n\n\u003Ch4>IE11 の対応\u003C/h4>\n\n特徴に「\u003Cstrong>IE を含めたプロダクションビルドも可能\u003C/strong>」と書きましたが、開発環境での IE の処理はできませんが、\u003Cstrong>プロダクションビルドが可能\u003C/strong>なので最終的には IE のサポートもされるという意味になります。\u003Cbr>つまり、ローカルの開発環境ではホットリロードしても変更が反映されないので、いちいち確認のためにビルドしなければいけません。\u003Cbr>\u003Cbr>ちなみに \u003Ca rel=\"noreferrer noopener\" href=\"https://github.com/vitejs/vite/tree/main/packages/plugin-legacy\" target=\"_blank\">@vitejs/plugin-legacy\u003C/a> というパッケージを使えば IE11 にも対応できますが、IE11 で対応していない機能は再現できないため、その場合は古い構文をトランスファイルするなどしなければいけません。\u003Cbr>\u003Cbr>個人的には IE の開発で Vite を採用するのは難しいように思います。\u003Cbr>\u003Cbr>\n\n\u003Ch2>使い方\u003C/h2>\n\n下のコマンドを入力し、\n\n\u003Cdiv class=\"hcb_wrap\">\u003Cpre class=\"prism undefined-numbers lang-plain\">\u003Ccode># NPMを使う場合\n$ npm create vite@latest\n\n# Yarnを使う場合\n$ yarn create vite\u003C/code>\u003C/pre>\u003C/div>\n\nあとはそのまま流れにそって設定を済ませれば簡単に開発環境が立ち上がります。\n\n\u003Cdiv class=\"wp-block-image\">\u003Cfigure class=\"aligncenter size-full is-resized\">\u003Cimg src=\"https://images.microcms-assets.io/assets/0eb919b93c74437dbd287ab903d9ea2c/470a9248014349e09cc4abcfc8215731/2022-03-cdd35cf009c2ce2cadd6a6b9e9eadcd5.png\" alt=\"\" class=\"wp-image-14550\" width=\"557\" height=\"506\"/>\u003C/figure>\u003C/div>\n\n速さについては\u003Cstrong> Vue やReact の開発環境とホットリロードの速さを比べてみると違いがよくわかる\u003C/strong>と思います。\u003Cbr>出力先やルートディレクトリの設定なども webpack と同じ感じで簡単に設定が可能です。\u003Cbr>\u003Cbr>\n\n\u003Ch2>所感\u003C/h2>\n\n速いのはもちろんそうですが、\u003Cstrong>ライブラリに依存せずプロジェクトに導入\u003C/strong>できそうな点が個人的には便利かなと思いました。\u003Cbr>\u003Cbr>一方で気になった点としては、\u003Cstrong>Vue や React での開発では全体が読み込まれるため、現在表示していないページで構文エラーがあった場合でもエラーが検知できる\u003C/strong>のですが、\u003Cstrong>Vite だと表示しているページのファイルしか読み込まれないためそれをしてくれません。\u003C/strong>なので\u003Cstrong>コミットする前に一度ビルドがうまくいっているかどうかを確認する必要がある\u003C/strong>と使っていて思いました。\u003Cbr>\u003Cbr>そういった実装になっているので、\u003Ccode>node-modules\u003C/code> に入っている大量の依存モジュールの読み込みもその都度行われるのですが、そこに関してはキャッシュする仕組みを持っているそうです。\u003Cbr>しかし、例えば手動で \u003Ccode>package.json\u003C/code> などを書き換えた際などに、そのキャッシュがクリアされないことがあるようなので、新しいパッケージを追加したとか何かを書き換えた時に意図しない挙動が起こっていたら一度キャッシュを消して確かめなければいけません。\u003Cbr>\u003Cbr>世の中的にはまだ IE 対応をやらないといけない機会もあるようなので少しプロジェクトに導入するのは躊躇されますが、 IE 非対応のプロジェクトやちょっとしたアプリケーションを作るときによかったら使ってみてください。",[],[],"2022-03-09T17:58:41+09:00",{"id":122,"createdAt":123,"updatedAt":124,"publishedAt":125,"revisedAt":124,"title":126,"content2":127,"eyecatch":128,"tag":132,"heading_list":36,"recruit":20,"is_html":20,"related_articles":137,"recommended_articles":138,"is_migration":20},"view-transition-api","2025-08-21T02:11:21.312Z","2025-09-18T04:47:12.986Z","2025-09-08T03:19:06.240Z","View Transition API でページ遷移アニメーションを試してみた","\u003Cp>家の周りで今年初めての蝉が鳴き始めました。\u003C/p>\u003Cp>この記事は、社内イベント「\u003Ca href=\"https://www.s2factory.co.jp/blog/tea/\" target=\"_blank\" rel=\"noopener noreferrer nofollow\">\u003Cu>お茶会\u003C/u>\u003C/a>」での発表内容をもとにまとめたものです。\u003Cbr>今回は弊社の yamashita が\u003Cstrong>「View Transition API」\u003C/strong>について話しました。\u003C/p>\u003Chr>\u003Cp>最近取り組んでいた案件で、\u003Cstrong>ページ遷移にアニメーションを取り入れる実装\u003C/strong>を行いました。\u003Cbr>フロントエンドは \u003Ca href=\"https://nextjs.org/\" target=\"_blank\" rel=\"noopener noreferrer\">Next.js\u003C/a> を用いて構築されており、クライアントの要望に沿った演出を実現するために、どの手法が最適かをいくつか検討した結果、\u003Cstrong>View Transition API\u003C/strong> を採用することにしました。\u003C/p>\u003Cp>実装を通して将来性を強く感じられたため、今回は「\u003Cstrong>View Transition API\u003C/strong>」について紹介したいと思います。\u003C/p>\u003Cp>\u003Cspan style=\"color: #9f9f9f\">※この記事は 2025年9月時点の情報をもとに執筆しています。ブラウザの対応状況などは今後変わる可能性があります。\u003C/span>\u003C/p>\u003Ch2 id=\"h7e47296fff\">View Transition API とは\u003C/h2>\u003Cp>\u003Cstrong>View Transition API\u003C/strong> は、\u003Cstrong>Web ページの遷移や要素の切り替えにアニメーションを挟める新しいブラウザ標準の API \u003C/strong>です。\u003Cbr>基本的には CSS をベースにしているためシンプルに使えますが、JavaScript を組み合わせてより細かい制御を行うことができます。\u003Cbr>\u003Cbr>\u003C/p>\u003Cdiv class=\"iframely-embed\">\u003Cdiv class=\"iframely-responsive\" style=\"height: 140px; padding-bottom: 0;\">\u003Ca href=\"https://developer.mozilla.org/ja/docs/Web/API/View_Transition_API\" data-iframely-url=\"https://cdn.iframe.ly/api/iframe?card=small&amp;url=https%3A%2F%2Fdeveloper.mozilla.org%2Fja%2Fdocs%2FWeb%2FAPI%2FView_Transition_API&amp;key=c271a3ec77ff4aa44d5948170dd74161\">\u003C/a>\u003C/div>\u003C/div>\u003Cscript async src=\"https://cdn.iframe.ly/embed.js\" charset=\"utf-8\">\u003C/script>\u003Cp>\u003Cbr>特徴としては、\u003C/p>\u003Cul>\u003Cli>\u003Cstrong>ページ全体をクロスフェードさせるなどの基本アニメーションはすぐに利用可能\u003C/strong>\u003C/li>\u003Cli>\u003Cstrong>遷移前後で DOM 構造が異なっていてもアニメーションを適用できる\u003C/strong>\u003C/li>\u003Cli>\u003Cstrong>@keyframes やアニメーションライブラリ(例：GSAP)と組み合わせて柔軟にカスタマイズ可能\u003C/strong>\u003C/li>\u003Cli>\u003Cstrong>ブラウザ標準機能なので軽量\u003C/strong>\u003C/li>\u003C/ul>\u003Cp>といった点があります。\u003Cbr>\u003Cbr>従来、ページ遷移に自然なアニメーションを加えるには DOM 管理や複雑な実装が必要でしたが、\u003Cstrong>SPA(Single-page application) だけでなく MPA(Multi-Page Application) にも対応\u003C/strong>し、標準 API として簡単に扱えるようになったのは大きな進歩と言えるでしょう。\u003C/p>\u003Cp>現時点(2025年9月)では、Chrome や Safari では利用できますが、\u003Cstrong>Firefox ではまだ対応していません\u003C/strong>。今後の実装状況に注目したいところです。\u003C/p>\u003Cp>実際の動きについては、\u003Ca href=\"https://view-transition-example.vercel.app/?utm_source=chatgpt.com\" target=\"_blank\" rel=\"noopener noreferrer nofollow\">\u003Cstrong>公式デモページ\u003C/strong>\u003C/a> でも確認できます。\u003C/p>\u003Ch2 id=\"h4d849f4362\">使い方\u003C/h2>\u003Cp>View Transition API を利用する方法は、大きく分けて 2 つあります。\u003C/p>\u003Ch3 id=\"hb02c0c0c2a\">CSS で有効化する方法\u003C/h3>\u003Cp>グローバルに読み込んでいる CSS に \u003Ccode>@view-transition\u003C/code> を記述するだけで、サイト全体に適用されます。\u003C/p>\u003Cpre>\u003Ccode class=\"language-html\">/* global.css */\n@view-transition {\n  navigation: auto;\n}\u003C/code>\u003C/pre>\u003Cp>この場合、\u003Cstrong>デフォルトの挙動はクロスフェード\u003C/strong>。特別な指定をしなくても、\u003Cstrong>ページ遷移時に前後の画面がふわっと切り替わるアニメーション\u003C/strong>になります。\u003C/p>\u003Ch3 id=\"h9a93d2415d\">JavaScript でトリガーする方法\u003C/h3>\u003Cp>もうひとつは JavaScript でトリガーする方法です。\u003C/p>\u003Cpre>\u003Ccode class=\"language-javascript\">function handleClick(e) {\n  // 非対応ブラウザ用のフォールバック\n  if (!document.startViewTransition) {\n    return;\n  }\n\n  // View Transition のトリガー\n  document.startViewTransition();\n}\u003C/code>\u003C/pre>\u003Cp>リンクやボタンのクリックイベントで \u003Ccode>document.startViewTransition()\u003C/code> を呼び出すことで、同様にページ遷移にアニメーションを組み込むことができます。\u003C/p>\u003Cp>ここからさらに、\u003Ccode>@keyframes\u003C/code> を使ったり、\u003Cstrong>GSAP などのアニメーションライブラリ\u003C/strong>を組み合わせることで、どちらの方法でも\u003Cstrong>独自の動きを加えることが可能\u003C/strong>です。\u003C/p>\u003Cp>仕組みとしては、\u003Cstrong>遷移前後で DOM のスナップショットを撮り、それらを切り替えることでアニメーションを実現\u003C/strong>しています。詳細は次に解説します。\u003C/p>\u003Ch2 id=\"h51081efd9f\">内部の仕組み\u003C/h2>\u003Cp>\u003Ccode>document.startViewTransition()\u003C/code> が呼ばれると、次のような流れで処理が行われます。\u003C/p>\u003Col>\u003Cli>遷移前の DOM のスナップショットが撮られる\u003C/li>\u003Cli>遷移後の DOM のスナップショットが、その前面に配置される\u003C/li>\u003Cli>デフォルトではクロスフェードで前後のスナップショットが入れ替わる\u003C/li>\u003C/ol>\u003Cfigure>\u003Cimg src=\"https://images.microcms-assets.io/assets/0eb919b93c74437dbd287ab903d9ea2c/2b9f28ea193448ff8fc921d07a39213b/%E3%82%B9%E3%83%8A%E3%83%83%E3%83%97%E3%82%B7%E3%83%A7%E3%83%83%E3%83%88%E3%81%AE%E4%BB%95%E7%B5%84%E3%81%BF.png?w=650&amp;h=335\" alt=\"スナップショットの仕組み\" width=\"650\" height=\"335\">\u003C/figure>\u003Cpre>\u003Ccode class=\"language-html\">&lt;html&gt;\n  ::view-transition\n    ::view-transition-group(root)\n      ::view-transition-image-pair(root)\n        ::view-transition-old(root) // 遷移前のスナップショット\n        ::view-transition-new(root) // 遷移後のページ\u003C/code>\u003C/pre>\u003Cp>このとき\u003Cstrong>ブラウザは自動的に擬似要素を生成し、アニメーション対象を管理\u003C/strong>します。\u003Cbr>たとえば \u003Ccode>::view-transition-old\u003C/code> は遷移前の状態、\u003Ccode>::view-transition-new\u003C/code> は遷移後の状態を指し、これらを使ってスタイルを当てることでカスタマイズが可能になるのです。\u003C/p>\u003Cp>つまり、\u003Cstrong>開発者は DOM の複雑な状態管理を意識せず、スナップショットと擬似要素をベースに自然なアニメーションを適用できる\u003C/strong>ようになっています。\u003C/p>\u003Ch3 id=\"hc586f16e95\">個別要素のアニメーション\u003C/h3>\u003Cp>カスタマイズの例として、\u003Cstrong>一覧ページのサムネイル画像と詳細ページの大きな画像を同じ要素としてつなぎ、自然な切り替えを実現する方法\u003C/strong>を紹介します。\u003C/p>\u003Cp>\u003Cstrong>HTML（一覧／詳細）\u003C/strong>\u003C/p>\u003Cpre>\u003Ccode class=\"language-html\">&lt;!-- 一覧画面 --&gt;\n&lt;div id=&quot;view-index&quot;&gt;\n  &lt;ul class=&quot;photo-list&quot;&gt;\n    &lt;li&gt;\n      &lt;div class=&quot;photo-link&quot;&gt;\n        &lt;img class=&quot;photo-thumb&quot; src=&quot;images/photo_1.webp&quot; width=&quot;640&quot; height=&quot;360&quot; alt=&quot;&quot; /&gt;\n      &lt;/div&gt;\n    &lt;/li&gt;\n  &lt;/ul&gt;\n&lt;/div&gt;\n\n&lt;!-- 詳細画面 --&gt;\n&lt;div id=&quot;view-detail&quot;&gt;\n  &lt;div class=&quot;detail-layout&quot;&gt;\n    &lt;div&gt;\n      &lt;img class=&quot;photo-detail&quot; src=&quot;images/photo_1.webp&quot; width=&quot;640&quot; height=&quot;360&quot; alt=&quot;&quot; /&gt;\n    &lt;/div&gt;\n  &lt;/div&gt;\n&lt;/div&gt;\n\u003C/code>\u003C/pre>\u003Cp>\u003Cbr>\u003Cstrong>CSS / SCSS\u003C/strong>\u003C/p>\u003Cpre>\u003Ccode class=\"language-css\">/* 一覧画面 */\n.photo-thumb {\n  view-transition-name: photo;\n}\n\n/* 詳細画面 */\n.photo-detail {\n  view-transition-name: photo;\n}\n\u003C/code>\u003C/pre>\u003Cp>上のコードのように\u003Cstrong>同じ名前(ここでは \u003C/strong>\u003Ccode>photo\u003C/code>\u003Cstrong>)を指定\u003C/strong>すると、\u003Ccode>document.startViewTransition()\u003C/code> の実行時にブラウザが遷移前後の要素を自動的に\u003Cstrong>ペアとして紐付け、共有要素トランジションとしてスムーズに接続\u003C/strong>します。\u003Cbr>\u003Cbr>このとき、CSS で特別に\u003Ccode>scale\u003C/code>や\u003Ccode>transform\u003C/code>を指定していなくても、\u003Cstrong>サムネイルから大きな画像への拡大が自動でなめらかに処理される\u003C/strong>のも便利です。\u003C/p>\u003Ch2 id=\"h9846e42613\">スライドを実装してみた\u003C/h2>\u003Cp>今回 View Transition API を試した背景には、\u003Cstrong>「Astro を触ってみたい」\u003C/strong>という動機もありました。\u003Cbr>\u003Ca href=\"https://docs.astro.build/en/guides/view-transitions/\" target=\"_blank\" rel=\"noopener noreferrer\">\u003Cstrong>Astro\u003C/strong>\u003C/a>\u003Cstrong> は比較的新しいフレームワークで、サーバーファーストの設計や MPA でもモダンな開発体験を提供できる\u003C/strong>点が特徴です。公式に View Transition API をサポートしているため、組み合わせて試すのにちょうど良い環境でした。\u003C/p>\u003Cp>Astro について詳しくは公式ドキュメントを参照してください。\u003Cbr>\u003Cbr>\u003C/p>\u003Cdiv class=\"iframely-embed\">\u003Cdiv class=\"iframely-responsive\" style=\"height: 140px; padding-bottom: 0;\">\u003Ca href=\"https://docs.astro.build/en/guides/view-transitions/\" data-iframely-url=\"https://cdn.iframe.ly/api/iframe?card=small&amp;url=https%3A%2F%2Fdocs.astro.build%2Fen%2Fguides%2Fview-transitions%2F&amp;key=c271a3ec77ff4aa44d5948170dd74161\">\u003C/a>\u003C/div>\u003C/div>\u003Cscript async src=\"https://cdn.iframe.ly/embed.js\" charset=\"utf-8\">\u003C/script>\u003Cp>\u003Cbr>この環境を利用して、実際にシンプルなスライドアプリを実装してみました。\u003Cbr>グリッド状に並んだスライドから任意のものをクリックすると、そのスライドが拡大表示され、他のスライドは左側に縦一列で並ぶ仕組みです。\u003Cbr>\u003Cbr>\u003C/p>\u003Cdiv style=\"left: 0; width: 100%; height: 0; position: relative; padding-bottom: 75%;\">\u003Ciframe src=\"https://www.youtube.com/embed/KQFim3v6wp8?rel=0\" style=\"top: 0; left: 0; width: 100%; height: 100%; position: absolute; border: 0;\" allowfullscreen scrolling=\"no\" allow=\"accelerometer *; clipboard-write *; encrypted-media *; gyroscope *; picture-in-picture *; web-share *;\">\u003C/iframe>\u003C/div>\u003Cp>\u003Cbr>以下が実際のコードの一部です。\u003C/p>\u003Cp>スライド一覧からクリックで拡大表示する仕組みは、クリックイベントから \u003Ccode>document.startViewTransition()\u003C/code> を呼び出し、その中で DOM を更新すると遷移が始まります。\u003C/p>\u003Cpre>\u003Ccode class=\"language-javascript\">gridButton?.addEventListener(&quot;click&quot;, async () =&gt; {\n  if (!document.startViewTransition) return;\n  const transition = document.startViewTransition(() =&gt; {\n    // 遷移後の DOM 更新処理\n  });\n});\u003C/code>\u003C/pre>\u003Cp>その上で、\u003Cstrong>CSS 側では \u003C/strong>\u003Ccode>::view-transition-group\u003C/code> や \u003Ccode>::view-transition-new / old\u003C/code> を用いて、遷移前後の要素にアニメーションを指定します。\u003C/p>\u003Cpre>\u003Ccode class=\"language-css\">:root {\n  view-transition-name: root;\n}\n\n::view-transition-group(image) {\n  animation-duration: 0.8s;\n  animation-timing-function: cubic-bezier(0.445, 0.05, 0.55, 0.95);\n  z-index: 10;\n}\n\n::view-transition-new(image),\n::view-transition-old(image) {\n  width: 100%;\n}\u003C/code>\u003C/pre>\u003Cp>\u003Cstrong>スライドは番号と名前を持つオブジェクトの配列として管理\u003C/strong>しており、\u003Cstrong>それだけで実装できたシンプルな構成\u003C/strong>です。\u003Cbr>それでも View Transition API を利用することで、スムーズで直感的な切り替えを実現できました。\u003C/p>\u003Ch2 id=\"hf6625118dc\">使ってみた感想\u003C/h2>\u003Ch3 id=\"h33537cc491\">良かったところ\u003C/h3>\u003Cul>\u003Cli>\u003Cstrong>実装が簡単\u003C/strong>\u003C/li>\u003Cli>\u003Cstrong>MPA でも SPA 的な遷移が可能\u003C/strong>\u003C/li>\u003Cli>\u003Cstrong>フレームワークでのサポートが進みつつある\u003C/strong>\u003Cul>\u003Cli>Astro では正式に対応しており、React / Next.js でも実験的に利用可能\u003C/li>\u003C/ul>\u003C/li>\u003C/ul>\u003Ch3 id=\"h043f0f0399\">課題に感じたところ\u003C/h3>\u003Cul>\u003Cli>\u003Ccode>view-transition-name\u003C/code> \u003Cstrong>はユニークである必要がある\u003C/strong>\u003Cul>\u003Cli>DOM ツリー内で重複するとトランジションが発火しなくなる\u003C/li>\u003C/ul>\u003C/li>\u003Cli>\u003Cstrong>ビューポート外の要素も対象になる\u003C/strong>\u003Cul>\u003Cli>制御が必要な場合は Intersection Observer API を使い、画面内の要素にだけ動的に \u003Ccode>view-transition-name\u003C/code> を付与するなどの工夫が必要\u003C/li>\u003C/ul>\u003C/li>\u003Cli>\u003Cstrong>Firefox は未対応\u003C/strong>\u003Cul>\u003Cli>非対応ブラウザでは通常の遷移になってしまうため、現時点では対応状況を考慮して利用しなければいけない\u003C/li>\u003C/ul>\u003C/li>\u003Cli>\u003Cstrong>遷移先が重いと不自然に見える場合がある\u003C/strong>\u003Cul>\u003Cli>プリフェッチや Next.js の \u003Ccode>Suspense\u003C/code> で改善可能\u003C/li>\u003C/ul>\u003C/li>\u003C/ul>\u003Cp>ちなみに、\u003Cstrong>処理が重い場合、アニメーションが走った後にページが急に切り替わる\u003C/strong>ように見えてしまうことがあります。\u003Cbr>これを避けるには、\u003Cstrong>事前にデータを取得しておく(プリフェッチ)\u003C/strong>ことでスムーズに見せることができるほか、\u003Cstrong>Next.js では \u003C/strong>\u003Ccode>Suspense\u003C/code>\u003Cstrong> を使ってコンポーネントを非同期で読み込み、初回レンダリングに含めないようにすることが可能\u003C/strong>です。\u003C/p>\u003Ch2 id=\"ha214098e44\">まとめ\u003C/h2>\u003Cp>View Transition API を使うことで、従来は実装が複雑だったページ遷移時のアニメーションを、シンプルなコードで実現できることが分かりました。\u003Cbr>特に \u003Cstrong>DOM 構造が異なるページ間でもスムーズに動きをつなげられる点や、MPA にも対応できる点は大きな強み\u003C/strong>です。\u003C/p>\u003Cp>実際に案件でも導入し、ユーザー体験を高める表現として利用できました。\u003Cbr>一方で、\u003Ccode>view-transition-name\u003C/code> の扱いには注意が必要だったり、Firefox が未対応であったりと、導入にあたって気をつける点もあります。\u003C/p>\u003Cp>それでも、すでに Astro や Next.js などのフレームワークでサポートが進みつつあり、標準機能として採用が広がればさらに使いやすくなるでしょう。\u003Cbr>今回の検証と実案件での経験を通じて、View Transition API は \u003Cstrong>今後のフロントエンド表現において実用性と将来性の両方を感じられる技術\u003C/strong>だと思いました。興味のある方はぜひ試してみてください。\u003C/p>\u003Cp>\u003C/p>\u003Cp>\u003C/p>",{"url":129,"height":130,"width":131},"https://images.microcms-assets.io/assets/0eb919b93c74437dbd287ab903d9ea2c/23c4792aa35b4ebc9cebdbad415e2ae7/%E3%82%A2%E3%82%A4%E3%82%AD%E3%83%A3%E3%83%83%E3%83%81.jpg",2333,3500,[133,134,135],{"id":27},{"id":16},{"id":136},"api",[],[],{"id":140,"createdAt":141,"updatedAt":142,"publishedAt":143,"revisedAt":142,"title":144,"content2":145,"eyecatch":146,"tag":150,"heading_list":20,"recruit":20,"is_html":20,"description":159,"related_articles":160,"recommended_articles":161,"is_migration":20},"claude-code-perl-typescript-migration","2026-05-29T08:21:55.189Z","2026-06-02T01:46:27.741Z","2026-06-02T01:45:24.758Z","テストがない 10 万行の Perl システムを、Claude Code で TypeScript へ書き換えた話","\u003Ch2 id=\"h586b90d155\">「AI が書いたコード、読んでますか？」\u003C/h2>\u003Cp>最近よく見かけるこの議論に、正直に答えます。\u003C/p>\u003Cp>\u003Cstrong>全部は読んでいません。10 万行、読みきれません。\u003C/strong>\u003C/p>\u003Cp>これは無責任に聞こえるかもしれません。「AI が書いたコードの 80% は負債だ」という研究もありますし、「理解していないコードをリリースするな」という意見はもっともだと思います。\u003C/p>\u003Cp>ただ、10 万行のコードを1行ずつ目で追って品質を担保するのは、\u003Cstrong>現実的に難しい\u003C/strong>ですよね。人間が書いたコードでも、全行レビューはしていないはずです。\u003C/p>\u003Cp>じゃあどうしたか。\u003C/p>\u003Cp>\u003Cstrong>読む代わりに、検証する仕組みを作りました。 AI に書かせたコードを、AIに検証させ続ける\u003C/strong>。24 時間、何百ラウンドも、バグが出なくなるまで。読んで理解する代わりに、正しさを証明する。\u003C/p>\u003Cp>この記事は、その話です。\u003C/p>\u003Ch2 id=\"h90ca2bc41d\">前提：テストがほぼない\u003C/h2>\u003Cp>私が担当しているのは、ある企業の EC サイトのバックエンドです。\u003C/p>\u003Cp>\u003Ca href=\"https://www.perl.org/\" target=\"_blank\" rel=\"noopener noreferrer\">Perl\u003C/a> で書かれていて、10 年以上動いています。認証、注文、決済、予約、LINE連携、クーポン。\u003Cstrong>API のエンドポイントは 133 個\u003C/strong>あります。\u003C/p>\u003Cp>そしてこのコードベースには、\u003Cstrong>まともな自動テストがほぼありませんでした。\u003C/strong>\u003C/p>\u003Cp>初めてソースを開いたときのことは覚えています。10 年分のビジネスロジックがぎっしり詰まっていて、でもそれを検証する手段が何もない。仕様書もない。何かを変えたとき、壊れたかどうかを知る方法がない。よくこれで 10 年動いてきたなと、率直に思いました。\u003C/p>\u003Cp>\u003Cstrong>これを \u003C/strong>\u003Ca href=\"https://www.typescriptlang.org/ja/\" target=\"_blank\" rel=\"noopener noreferrer\">\u003Cstrong>TypeScript\u003C/strong>\u003C/a>\u003Cstrong> に書き換えろ、というのが今回のミッション\u003C/strong>でした。\u003C/p>\u003Ch2 id=\"h460484bf92\">普通にやったら詰みます\u003C/h2>\u003Cp>テストのないコードの移植は、ソフトウェアエンジニアリングでも最悪の部類に入る仕事です。\u003C/p>\u003Cp>教科書的にやるなら、まず既存の Perl コードにテストを書きます。挙動を固めてから、TypeScript で書き直す。でも 10 年分のコードにテストを書く工数は、移植そのものと同じかそれ以上かかります。しかも、Perl の挙動をちゃんと理解してテストに落とせる人材は限られています。\u003C/p>\u003Cp>詰んでます。\u003C/p>\u003Ch2 id=\"h4677704352\">Claude Code に「書かせる」のは1週間で終わりました\u003C/h2>\u003Cp>Claude Code には、\u003Cstrong>カスタムスラッシュコマンド\u003C/strong>という機能があります。\u003Cstrong>自分でコマンドを定義して、繰り返し使える仕組み\u003C/strong>です。\u003C/p>\u003Cp>私はまず \u003Ccode>/migrate-endpoint\u003C/code> というコマンドを作りました。\u003C/p>\u003Cp>Perl のコントローラのファイルパスを渡すと、Claude Code が関連するモデルやエンティティ、DB スキーマまで全部読みに行って、TypeScript の実装コードとテストを一気に生成してくれます。\u003C/p>\u003Cfigure>\u003Cimg src=\"https://images.microcms-assets.io/assets/0eb919b93c74437dbd287ab903d9ea2c/285f776615bd4583a5177b8d1c440a90/migrate-endpoint-flow.png\" alt=\"\" width=\"1448\" height=\"1086\">\u003C/figure>\u003Cp>\u003Cstrong>テストがないなら、テストごと作ればいい。\u003C/strong>\u003C/p>\u003Cp>この割り切りで、\u003Cstrong>移植前にテストを書くという工程を丸ごとスキップ\u003C/strong>できました。\u003C/p>\u003Cp>133 エンドポイント、約 10 万行。実装が終わるまで、約 1 週間でした。最初のエンドポイントが動いたときは「本当にこれでいけるのか？」という気持ちが正直ありましたが、\u003Cstrong>2 つ目、3 つ目と進むうちに、コマンドの精度がどんどん上がっていく\u003C/strong>のがわかりました。\u003C/p>\u003Ch2 id=\"h1a681fd8e2\">でも「書けた」は「正しい」じゃない\u003C/h2>\u003Cp>1 週間で 10 万行。数字だけ見ればすごいです。\u003C/p>\u003Cp>でも、テストのほぼない Perl から移植したコードです。\u003Cstrong>「動くけど微妙に違う」コードが量産されている可能性\u003C/strong>は大いにあります。\u003C/p>\u003Cp>Perl は未定義値を空文字やゼロとして黙って処理します。TypeScript ではそうはいきません。タイムゾーンの扱いも違う。数値と文字列の暗黙変換もある。こういう差異は、普通のテストでは見つかりません。\u003C/p>\u003Cp>そこで、これも仕組みで解決することにしました。\u003C/p>\u003Ch2 id=\"h7480bb35d9\">パリティ監査という仕組みを作りました\u003C/h2>\u003Cp>もう 1 つのカスタムコマンド、\u003Ccode>/parity-audit\u003C/code> を作りました。\u003C/p>\u003Cp>やることはシンプルです。\u003C/p>\u003Cul>\u003Cli>\u003Cstrong>Perl のコードと TypeScript のコードを突き合わせて、挙動の差異を見つける\u003C/strong>\u003C/li>\u003Cli>\u003Cstrong>差異があればバグとして修正し、テストを追加する\u003C/strong>\u003C/li>\u003C/ul>\u003Cp>このコマンドを実行すると、\u003Cstrong>1 ラウンドで 20 の AI エージェントが並列に起動\u003C/strong>し、それぞれが違う切り口でコードを調べていきます。\u003C/p>\u003Cfigure>\u003Cimg src=\"https://images.microcms-assets.io/assets/0eb919b93c74437dbd287ab903d9ea2c/e1d8b1dc72f44fcf9f367d8dc3b852b9/parity-audit-flow.png\" alt=\"\" width=\"1447\" height=\"1087\">\u003C/figure>\u003Cp>あるエージェントはエンドポイント単位で、リクエストからレスポンスまでのコードパスを 1 行ずつ比較しています。別のエージェントは DB アクセス層を見ていて、クエリの条件やソート順、NULL の扱いをチェックしています。\u003C/p>\u003Cp>決済まわりを専門で見るエージェントもいます。税計算、クーポン適用、支払いステータス。1 円でもズレたらアウトの領域です。認証・セッション管理を集中的に検証するエージェントもいて、ログインや SSO、MFA といったセキュリティに直結する処理を重点的に見ます。\u003C/p>\u003Cp>日時処理、暗号化、バーコード生成といった「言語が変わると挙動が変わりやすい」ユーティリティも、専用のエージェントが横断的にチェックしています。\u003C/p>\u003Cp>これだけの観点を人間が毎回カバーするのは、まず無理だと思います。\u003C/p>\u003Ch2 id=\"h9def901947\">「10回連続クリーン」になるまで止めません\u003C/h2>\u003Cp>パリティ監査には、1 つ\u003Cstrong>ルール\u003C/strong>を課しました。\u003C/p>\u003Cp>\u003Cstrong>連続10回、差異ゼロが出るまで回し続ける。\u003C/strong>\u003C/p>\u003Cp>1ラウンドでもバグが見つかったら、カウンタはゼロに戻ります。修正して、また最初から。10 回連続でクリーンになって、初めて「OK」です。\u003C/p>\u003Cp>このコマンドを夜間も含めて走らせ続けました。\u003C/p>\u003Cp>\u003Cstrong>2.5 ヶ月間。\u003C/strong>\u003C/p>\u003Ch2 id=\"h29bb0ce50d\">監査 379 ラウンド、800件以上のバグを修正\u003C/h2>\u003Cp>結果として、\u003Cstrong>379 ラウンドの監査を回し、800 件以上のバグ\u003C/strong>を見つけて直しました。\u003C/p>\u003Cp>最初のほうはラウンドごとにバグが山のように出てきて、正直なところ気が遠くなりました。でも修正を重ねるうちに、だんだんクリーンなラウンドが増えてきます。連続クリーンが 5 回、6 回と伸びていくのを見ているときは、ちょっとした達成感がありました。\u003C/p>\u003Cp>中には、\u003Cstrong>移植元の Perl 側に何年も潜んでいたバグが見つかったケースも\u003C/strong>ありました。テストがなかったから、誰も気づかなかったバグです。\u003Cstrong>パリティ監査がなければ、そのまま TypeScript 側にも引き継がれていた\u003C/strong>でしょう。\u003C/p>\u003Ch2 id=\"h3884ede80a\">これで何が変わったか\u003C/h2>\u003Cp>移植前と移植後で、テストの状況は一変しました。\u003C/p>\u003Cp>\u003Cstrong>移植後のテストは 1,419 件。\u003C/strong> しかも全部パスしています。移植プロジェクトとしてだけでなく、テスト資産の構築としても大きな成果だと思っています。\u003C/p>\u003Cp>そして、1,419 件のテストがあるということは、ここからのリファクタリングが容易になるということでもあります。\u003Cstrong>テストがなかった頃は怖くて触れなかったコードを、安心して改善できる。\u003C/strong>移植がゴールではなく、ここからがスタートです。\u003C/p>\u003Cfigure>\u003Cimg src=\"https://images.microcms-assets.io/assets/0eb919b93c74437dbd287ab903d9ea2c/faaa3c46c0cf4ac6b95524e19d81e50c/migration-audit-results.png\" alt=\"\" width=\"1448\" height=\"1086\">\u003C/figure>\u003Ch2 id=\"h49ccabfba5\">そして、人間の手で確かめました\u003C/h2>\u003Cp>\u003Cstrong>最後に、フロントエンドとつないで E2E テスト\u003C/strong>をしました。\u003C/p>\u003Cp>これは人間が手で動かすテストです。実際のブラウザで、実際の画面を操作して、注文を入れて、決済して、予約して。AI ではなく、人間の目と手で確かめました。\u003C/p>\u003Cp>\u003Cstrong>不具合は見つかりませんでした。\u003C/strong>\u003C/p>\u003Cp>10 万行。テストがほぼない状態からの移植。AI が書いて、AI が検証して、最後に人間が触って、壊れていなかった。\u003C/p>\u003Cp>正直、自分でもちょっと信じられませんでした。\u003C/p>\u003Ch2 id=\"h42e2556fbc\">Claude Codeの使い方として伝えたいこと\u003C/h2>\u003Cp>この経験を通じて、Claude Code の使い方について 3 つのことがわかりました。\u003C/p>\u003Cul>\u003Cli>\u003Cstrong>カスタムコマンドで「仕組み」にする\u003C/strong>\u003Cul>\u003Cli>属人的な「上手いプロンプトの書き方」ではなく、誰でも実行できるコマンドとして定義します。これで品質が人に依存しなくなります。\u003C/li>\u003C/ul>\u003C/li>\u003Cli>\u003Ca href=\"http://CLAUDE.md\">\u003Cstrong>CLAUDE.md\u003C/strong>\u003C/a>\u003Cstrong> に知識を貯める\u003C/strong>\u003Cul>\u003Cli>プロジェクト固有のルール（アーキテクチャ、命名規則、エラーコードなど）を CLAUDE.md ファイルに書いておきます。セッションが変わっても、Claude Code はこれを読んでくれます。移植が進むほどこのファイルが育ち、後半フェーズほど手戻りが減りました。\u003C/li>\u003C/ul>\u003C/li>\u003Cli>\u003Cstrong>品質保証を「計算資源」の問題にする\u003C/strong>\u003Cul>\u003Cli>1 ラウンド 20 エージェント × 379 ラウンド。この物量の検証を人間がやるのは無理です。でもコマンドにして 24 時間回せば、品質保証は工数の問題ではなく計算資源の問題になります。\u003C/li>\u003C/ul>\u003C/li>\u003C/ul>\u003Ch2 id=\"h5b6299b788\">「読んでない」のその先へ\u003C/h2>\u003Cp>冒頭に戻ります。\u003C/p>\u003Cp>\u003Cstrong>AI が書いたコード、読んでますか？\u003C/strong>\u003C/p>\u003Cp>私の答えは変わりません。全部は読んでいません。でも、全部検証しました。20 エージェント並列で、379 ラウンド、800 件のバグを潰して、最後に人間の手で E2E テストして、不具合ゼロ。\u003C/p>\u003Cp>もちろん、コードを読んで理解すること自体の価値を否定するつもりはありません。ただ、「読む」以外にも品質を担保する手段はあるはずです。\u003C/p>\u003Cp>\u003Cstrong>読むか読まないかではなく、正しさをどう証明するか。\u003C/strong> その仕組みを設計できるかどうかが、AI 時代のエンジニアリングで問われることなんじゃないかと思っています。\u003C/p>\u003Ch2 id=\"hc96909f29a\">この 3 ヶ月は長いのか\u003C/h2>\u003Cp>AI コーディングの事例として、「数時間でアプリを作った」「週末で完成した」といった話をよく見かけます。それと比べると、3 ヶ月という数字は地味に映るかもしれません。\u003C/p>\u003Cp>でも、考えてみてください。\u003Cstrong>テストのほぼない 10 万行のプロダクションコードを、133 エンドポイントの挙動を 1 つも壊さずに別言語へ移植\u003C/strong>する。普通にやったら 1 年はかかるプロジェクトです。見積もりの段階で「現実的じゃない」と却下されてもおかしくない。\u003C/p>\u003Cp>\u003Cstrong>それを 3 ヶ月で、しかも人間の E2E テストで不具合ゼロという品質で完遂\u003C/strong>できました。\u003C/p>\u003Cp>同じような課題を抱えている方は、少なくないと思います。テストのないレガシーシステム、移植したいけど踏み切れない、人手も時間も足りない。今回のアプローチは、そうしたプロジェクトにも十分に適用できると考えています。\u003C/p>\u003Cp>\u003Cstrong>レガシーシステムの移行でお困りの方がいらっしゃれば、ぜひご相談ください。\u003C/strong>\u003Cbr>\u003Cbr>\u003C/p>\u003Chr>\u003Cp>\u003Cspan style=\"color: #6c6c6c\">注記：本記事で紹介した \u003C/span>\u003Ccode>/migrate-endpoint\u003C/code>\u003Cspan style=\"color: #6c6c6c\">、\u003C/span>\u003Ccode>/parity-audit\u003C/code>\u003Cspan style=\"color: #6c6c6c\"> は Claude Code のカスタムスラッシュコマンド機能で構築したものです。\u003C/span>\u003C/p>",{"url":147,"height":148,"width":149},"https://images.microcms-assets.io/assets/0eb919b93c74437dbd287ab903d9ea2c/1632b87e5aaa4944a854ccc430cdb89e/%E3%82%A2%E3%82%A4%E3%82%AD%E3%83%A3%E3%83%83%E3%83%81.png",941,1672,[151,153,155,157,158],{"id":152},"ai",{"id":154},"typescript",{"id":156},"backend",{"id":27},{"id":52},"テストがほぼない10万行のPerl製ECバックエンドを、Claude Codeを使ってTypeScriptへ移植。カスタムコマンドによる実装生成とパリティ監査で、379ラウンドの検証、800件以上のバグ修正、1,419件のテスト構築まで行った取り組みを紹介します。",[],[],[],{"contents":164,"totalCount":187,"offset":188,"limit":189},[165,175,177],{"id":166,"eyecatch":167,"publishedAt":171,"createdAt":172,"title":173,"is_html":20,"content2":174},"aws-saa-certification",{"url":168,"height":169,"width":170},"https://images.microcms-assets.io/assets/0eb919b93c74437dbd287ab903d9ea2c/981a7c52ce36479e8fdf6085a3443ad5/aws-saa-certification.png",909,1731,"2026-06-22T04:53:58.852Z","2026-06-19T04:35:15.826Z","AWS Certified Solutions Architect - Associate を受験した話","\u003Cp>悪夢しか見ないおかげで、悪夢についてのインタビューを受けました。\u003C/p>\u003Cp>この記事は、社内イベント「\u003Ca href=\"https://www.s2factory.co.jp/blog/tea/\" target=\"_blank\" rel=\"noopener noreferrer\">お茶会\u003C/a>」での発表内容をもとにまとめたものです。\u003Cbr>今回は、弊社の otsuka が\u003Cstrong>「AWS Certified Solutions Architect - Associate」\u003C/strong>の受験について話しました。\u003C/p>\u003Chr>\u003Cp>\u003Cbr>今年の 4 月に、\u003Ca href=\"https://aws.amazon.com/jp/\" target=\"_blank\" rel=\"noopener noreferrer\">AWS\u003C/a> 認定試験のひとつである\u003Cstrong>「\u003C/strong>\u003Ca href=\"https://aws.amazon.com/jp/certification/certified-solutions-architect-associate/\" target=\"_blank\" rel=\"noopener noreferrer\">\u003Cstrong>AWS Certified Solutions Architect - Associate（SAA）\u003C/strong>\u003C/a>\u003Cstrong>」\u003C/strong>を受験しました。\u003Cbr>受験に向けてどのように勉強したのか、実際に受けてみてどうだったのかを振り返りたいと思います。\u003C/p>\u003Ch2 id=\"h66b306ae14\">試験について\u003C/h2>\u003Cp>AWS 認定試験には、いくつかの\u003Cstrong>レベル\u003C/strong>があります。\u003C/p>\u003Cul>\u003Cli>\u003Cstrong>Foundational\u003C/strong>：AWS 初心者向け\u003C/li>\u003Cli>\u003Cstrong>Associate\u003C/strong>：実務で AWS に触れている人向け\u003C/li>\u003Cli>\u003Cstrong>Professional\u003C/strong>：上級エンジニア・アーキテクト向け\u003C/li>\u003Cli>\u003Cstrong>Specialty\u003C/strong>：特定分野の専門家向け\u003C/li>\u003C/ul>\u003Cp>今回受験したのは、Associate レベルの \u003Cstrong>AWS Certified Solutions Architect - Associate(SAA)\u003C/strong> です。\u003C/p>\u003Cp>SAA では、AWS を使ったシステム設計を中心に、以下のような知識が問われます。\u003C/p>\u003Cul>\u003Cli>\u003Cstrong>高可用性\u003C/strong>\u003C/li>\u003Cli>\u003Cstrong>セキュリティ\u003C/strong>\u003C/li>\u003Cli>\u003Cstrong>コスト最適化\u003C/strong>\u003C/li>\u003Cli>\u003Cstrong>ネットワーク\u003C/strong>\u003C/li>\u003C/ul>\u003Cp>\u003Cstrong>試験時間は 130 分で、問題数は 65 問。1000 点満点のうち 720 点以上で合格\u003C/strong>です。\u003Cbr>ちなみに受験料は 22,000 円。申し込みには AWS のアカウントとは別のアカウントが必要で、少し手間取ったところもありました。\u003C/p>\u003Ch2 id=\"h89be2e6da8\">AWS認定試験を受けようと思った理由\u003C/h2>\u003Cp>受験前は、業務で AWS に触る機会はあるものの、\u003Cstrong>細かいオプションやサービスごとの使い分けまでは十分に理解できていない状態\u003C/strong>でした。\u003Cbr>なんとなく触っている状態から、\u003Cstrong>もう少し体系的に知識を身につけたい\u003C/strong>と思ったことが、受験しようと思ったきっかけです。\u003C/p>\u003Cp>また、S2ファクトリーには\u003Cstrong>社員のスキルアップを支援する資格試験支援制度\u003C/strong>があります。\u003Cstrong>試験費用のほか、勉強に必要な参考書や有料講座なども補助対象になる\u003C/strong>制度です。\u003C/p>\u003Cp>AWS 認定試験は受験料が決して安くないため、こうした制度があることで挑戦しやすくなったと感じています。\u003C/p>\u003Ch2 id=\"h59fab18ef3\">勉強方法\u003C/h2>\u003Cp>AWS 認定試験の体験談を見ていると、\u003Ca href=\"https://www.udemy.com/\" target=\"_blank\" rel=\"noopener noreferrer\">Udemy\u003C/a> を使って勉強している人が多い印象でした。\u003Cbr>ただ、動画で学ぶのがあまり得意ではなかったため、別の方法で勉強することに。\u003C/p>\u003Cp>最初に試したのは参考書です。見やすそうだと思って選んだものの、読んでいてもなかなか頭に入らず、思うように勉強が進みませんでした。\u003C/p>\u003Cp>そこで使い始めたのが、\u003Ca href=\"https://mondai.ping-t.com/g\" target=\"_blank\" rel=\"noopener noreferrer\">\u003Cstrong>Ping-t \u003C/strong>\u003C/a>\u003Cstrong>という問題演習サイト\u003C/strong>です。AWS 認定試験の受験経験がある社内メンバーに教えてもらったサイトで、最終的にはこれを中心に勉強しました。\u003Cbr>\u003Cbr>\u003C/p>\u003Cdiv class=\"iframely-embed\">\u003Cdiv class=\"iframely-responsive\" style=\"height: 140px; padding-bottom: 0;\">\u003Ca href=\"https://www.s2factory.co.jp/blog/aws-certified/\" data-iframely-url=\"https://iframely.net/api/iframe?card=small&amp;url=https%3A%2F%2Fwww.s2factory.co.jp%2Fblog%2Faws-certified%2F&amp;key=c271a3ec77ff4aa44d5948170dd74161\">\u003C/a>\u003C/div>\u003C/div>\u003Cscript async src=\"https://iframely.net/embed.js\" charset=\"utf-8\">\u003C/script>\u003Cp>\u003Cbr>自分の勉強スタイルとしては、こんな感じです。\u003C/p>\u003Cul>\u003Cli>Ping-t で問題を解く\u003C/li>\u003Cli>解説を読んで、分からなかった内容を確認する\u003C/li>\u003Cli>要点がまとまったページを iPad で書き写して暗記\u003C/li>\u003C/ul>\u003Cp>スマホで少しずつ進められるところが自分には合っていて、最終的には \u003Cstrong>800 問\u003C/strong>ほど解きました。\u003C/p>\u003Cp>Ping-t には無料で解ける問題もありますが、SAA の問題集をしっかり進めたかったので、\u003Cstrong>有料プランを利用\u003C/strong>しました。\u003Cbr>当初は 1 ヶ月で進めるつもりでしたが、途中で受験日を少し後ろにずらしたこともあり、結果的には 2 ヶ月ほど使っています。\u003C/p>\u003Ch2 id=\"haaf1d50dfc\">試験内容\u003C/h2>\u003Cp>試験では、サービス自体の知識をそのまま問うというより、\u003Cstrong>「こういう場面ではどのサービスを使うのがよいか」を考える問題\u003C/strong>が多い印象。\u003C/p>\u003Cp>実際には、このあたりの内容が出ました。\u003C/p>\u003Cul>\u003Cli>可用性・耐障害性\u003C/li>\u003Cli>ディザスタリカバリ\u003C/li>\u003Cli>コスト最適化\u003C/li>\u003Cli>セキュリティ（IAM / KMS / VPC）\u003C/li>\u003Cli>サーバーレス構成\u003C/li>\u003Cli>S3のストレージクラス\u003C/li>\u003Cli>SQS / SNS を使った構成\u003C/li>\u003Cli>S3 / EBS / EFS などのストレージ選択\u003C/li>\u003C/ul>\u003Cp>選択肢の中には「間違いではないけれど、そこまでしなくてもよさそう」と思うものもあり、問題文を読みながら、\u003Cstrong>要件に対して過不足のない答えを選んでいく\u003C/strong>イメージです。\u003C/p>\u003Cp>本番では、勉強した内容も多く出てきて安心した一方で、EventBridge のイベントバスなど、あまり触れていなかった内容もあり、まだまだ知らないことが多いと感じました。\u003C/p>\u003Ch2 id=\"hf980b243ef\">試験当日\u003C/h2>\u003Cp>認定試験は、\u003Ca href=\"https://aws.amazon.com/jp/certification/certification-prep/testing/\" target=\"_blank\" rel=\"noopener noreferrer\">\u003Cstrong>テストセンターで受ける方法\u003C/strong>と、自宅やオフィスから\u003Cstrong>オンライン監督付きで受ける方法\u003C/strong>\u003C/a>があります。\u003Cbr>オンラインの場合は、画面共有と Web カメラを通じて試験監督員の確認を受けながら受験するそうです。\u003Cbr>\u003Cbr>私は、\u003Ca href=\"https://www.pearsonvue.com/jp/ja/test-takers.html\" target=\"_blank\" rel=\"noopener noreferrer\">ピアソンVUE\u003C/a>のテストセンターで受けることにしました。\u003Cbr>\u003Cstrong>テストセンターは池袋や新宿など都内にも複数\u003C/strong>ありますが、受けられる会場は試験日や空き状況によって変わります。予約時に検索して選ぶ形になるため、最終的には予約メールに書かれた住所を確認するのが確実です。\u003Cbr>\u003Cbr>今回、一番焦ったのがまさにその\u003Cstrong>会場確認\u003C/strong>でした。\u003Cbr>予約メールには「IKEBUKUROEKI NISHIGUCHI TEST CENTER」と書かれていて、以前に別の試験を受けた会場だと思い込んで向かったのですが、建物に入ると「ここは SAA の試験会場ではありません」という張り紙が。わざわざ張り紙があるということは、同じように間違える人が多いのかもしれません。\u003C/p>\u003Cp>改めてメールの住所を確認すると、実際の会場は別の場所。幸いにも 5 分ほどの距離だったため、小走りで移動し、無事に受験できました。\u003C/p>\u003Cp>会場では\u003Cstrong>顔写真の撮影や持ち物のチェック\u003C/strong>を済ませてから席につきます。\u003Cbr>試験の時間配分がわからなかったため、まずは全体を一周し、残った時間でもう一度見直す形で臨んだのですが、時間的にも余裕と、ある程度の手応えを持って終えることができたので良かったです。\u003C/p>\u003Ch2 id=\"hb6509f48bf\">結果\u003C/h2>\u003Cp>試験結果はその場ではわからず、後日メールで通知されます。\u003C/p>\u003Cp>\u003Cstrong>翌日の朝には、「合格バッジを獲得しました」というメール\u003C/strong>が届きました。合格したことはわかるのですが、正式な結果通知より先にバッジの案内が来たので、できれば順番は逆がよかったなと思いました。\u003C/p>\u003Cp>その後、試験結果の案内メールも届き、無事に合格していることを確認。\u003C/p>\u003Cp>スコアは 832 点。合格ラインは 720 点なので、\u003Cstrong>ある程度余裕を持って合格できたのは嬉しかった\u003C/strong>です。\u003C/p>\u003Cfigure>\u003Cimg src=\"https://images.microcms-assets.io/assets/0eb919b93c74437dbd287ab903d9ea2c/fe14942ccf194884842f13dbfde09ae0/%E5%90%88%E6%A0%BC%E9%80%9A%E7%9F%A5.png?w=600&amp;h=483\" alt=\"合格通知\" width=\"600\" height=\"483\">\u003C/figure>\u003Ch2 id=\"ha214098e44\">まとめ\u003C/h2>\u003Cp>受験してみて、AWS についてまだ知らないことがたくさんあるなと思いました。\u003C/p>\u003Cp>業務で使うことはあっても、サービスごとの違いや、どういう場面でどの構成を選ぶのかを改めて整理するきっかけは意外と少ないものです。試験勉強を通して、なんとなく触っていた部分を見直すよい機会になりました。\u003C/p>\u003Cp>ちなみに、AWS 認定試験は、合格すると次回以降の試験で使える\u003Cstrong>半額チケット\u003C/strong>がもらえるようです。Developer Associate や DevOps Engineer Professional、Solutions Architect Professional など、\u003Cstrong>実務に関わりそうな試験も気になっているので、今後また別の試験にも挑戦\u003C/strong>してみたいと思います。\u003C/p>",{"id":140,"eyecatch":176,"publishedAt":143,"createdAt":141,"title":144,"is_html":20,"content2":145},{"url":147,"height":148,"width":149},{"id":178,"eyecatch":179,"publishedAt":183,"createdAt":184,"title":185,"is_html":20,"content2":186},"paris-fashion-style-diagnosis",{"url":180,"height":181,"width":182},"https://images.microcms-assets.io/assets/0eb919b93c74437dbd287ab903d9ea2c/b2e054339c6d46ca81c5a48071b7018b/thumb.jpg",3648,5472,"2026-05-22T04:35:29.476Z","2026-05-08T02:51:21.139Z","パリ流ファッションスタイル診断を受診した話","\u003Cp>久しぶりにプリンを見たら美味しそうだなと思いました。\u003C/p>\u003Cp>この記事は、社内イベント「\u003Ca href=\"https://www.s2factory.co.jp/blog/tea/\" target=\"_blank\" rel=\"noopener noreferrer nofollow\">\u003Cu>お茶会\u003C/u>\u003C/a>」での発表内容をもとにまとめたものです。\u003Cbr>今回は、弊社の tsujikado が\u003Cstrong>「パリ流ファッションスタイル診断」\u003C/strong>について話しました。\u003C/p>\u003Chr>\u003Cp>\u003Cbr>\u003Cstrong>パリ流ファッションスタイル診断\u003C/strong>というものを受診してきました。\u003Cbr>今回は、パリ流ファッションスタイル診断とはどのようなものなのか、実際に受けてみてどうだったのかを紹介します。\u003C/p>\u003Ch2 id=\"h80fac557c9\">パリ流ファッションスタイル診断とは？\u003C/h2>\u003Cp>パリ流ファッションスタイル診断は、いわゆる\u003Cstrong>イメージコンサルティングの一種\u003C/strong>です。\u003C/p>\u003Cp>イメージコンサルティングには、たとえば次のような診断があります。\u003C/p>\u003Cul>\u003Cli>\u003Cstrong>パーソナルカラー診断\u003C/strong>\u003Cul>\u003Cli>似合う色の傾向を見る診断\u003C/li>\u003C/ul>\u003C/li>\u003Cli>\u003Cstrong>骨格診断\u003C/strong>\u003Cul>\u003Cli>身体のラインや質感から、似合う服の形・素材を見る診断\u003C/li>\u003C/ul>\u003C/li>\u003Cli>\u003Cstrong>顔タイプ診断\u003C/strong>\u003Cul>\u003Cli>顔立ちの印象から、似合う服のテイストや髪型を見る診断\u003C/li>\u003C/ul>\u003C/li>\u003C/ul>\u003Cp>パリ流ファッションスタイル診断も、それらに近い診断で、\u003Cstrong>自分に似合うファッションの方向性を見ていくもの\u003C/strong>です。\u003C/p>\u003Cp>\u003Cstrong>フランス国家認定イメージコンサルタントの\u003C/strong>\u003Ca href=\"https://www.timage-paris.com/about-me/\" target=\"_blank\" rel=\"noopener noreferrer\">\u003Cstrong>トモミさん\u003C/strong>\u003C/a>\u003Cstrong>が開発したメソッド\u003C/strong>で、\u003Cstrong>10タイプ\u003C/strong>の中から、その人に合うファッションスタイルを導き出します。\u003C/p>\u003Cp>「パリ流」という名前は、創始者であるトモミさんがパリで学ばれていたことや、フランスでは内面やパーソナリティを大切にする考え方があることに由来しているそうです。そのため、この診断でも外見だけではなく、その人の雰囲気や内面も含めて見ていきます。\u003C/p>\u003Cp>現在は\u003Cstrong>トモミさんから直接学んだ門下生しか診断できない\u003C/strong>そうで、診断士の数も多くないため、受けられるサロンも限られています。\u003C/p>\u003Ch3 id=\"h5d88dedbcc\">パリ流で見る「スタイル」とは？\u003C/h3>\u003Cp>パリ流ファッションスタイル診断でいう「スタイル」とは、\u003Cstrong>その人が持つイメージや雰囲気\u003C/strong>のことを指します。\u003C/p>\u003Cp>このスタイルは、\u003Cstrong>5 つのカテゴリー、10タイプ\u003C/strong>に分けられています。10タイプを簡単に整理すると、次のようになります。\u003Cbr>\u003Cbr>\u003C/p>\u003Cfigure>\u003Cimg src=\"https://images.microcms-assets.io/assets/0eb919b93c74437dbd287ab903d9ea2c/afa5124f592e4b87b50240d188f7759f/%E3%82%B9%E3%82%BF%E3%82%A4%E3%83%AB.png?w=800&amp;h=999\" alt=\"スタイル一覧\" width=\"800\" height=\"999\">\u003C/figure>\u003Cp>\u003Cbr>この診断では、10タイプの中から\u003Cstrong>「メイン」「サブ」「アクセント」\u003C/strong>を決めていきます。\u003C/p>\u003Cul>\u003Cli>\u003Cstrong>メイン\u003C/strong>\u003Cul>\u003Cli>一番強く出る要素。第一印象に近いもの。\u003C/li>\u003C/ul>\u003C/li>\u003Cli>\u003Cstrong>サブ\u003C/strong>\u003Cul>\u003Cli>メインを補い、その人らしい個性を出すもの。\u003C/li>\u003C/ul>\u003C/li>\u003Cli>\u003Cstrong>アクセント\u003C/strong>\u003Cul>\u003Cli>メインとサブだけでは表現しきれない部分を補うもの。人によっては、アクセントが付かない場合もあります。\u003C/li>\u003C/ul>\u003C/li>\u003C/ul>\u003Cp>つまり、パリ流ファッションスタイル診断は、10タイプの中から自分に合う要素を組み合わせて、自分らしいファッションの方向性を見ていく診断です。\u003C/p>\u003Cp>\u003Cstrong>「自分のスタイルに合ったファッションをすることで、その人の魅力をより引き出せる」\u003C/strong>という考え方がベースになっています。\u003C/p>\u003Ch3 id=\"ha56011c6a9\">スタイルの組み合わせでどう変わるのか\u003C/h3>\u003Cp>説明だけでは、少しイメージしづらいかもしれません。\u003C/p>\u003Cp>たとえば、\u003Cstrong>メインが「ナチュラル」、サブが「クリエイティブ」\u003C/strong>の人の場合。\u003C/p>\u003Cp>ナチュラルの要素だけで全身をまとめると、自然由来のゆったりとした服装や、無造作な髪型などがイメージされます。ただ、それだけでまとめてしまうと、\u003Cstrong>クリエイティブの要素を持つ人の個性までは引き出しきれない\u003C/strong>ことがあります。\u003C/p>\u003Cp>そこで、サブであるクリエイティブの要素を少し足していきます。たとえば、服の雰囲気とは違うビビッドなアイテムを取り入れたり、少し個性的な着こなしをしてみたりします。\u003C/p>\u003Cp>そうすることで、ナチュラルをベースにしながらクリエイティブの要素も入り、垢抜けた印象になったり、抜け感が出たりして、\u003Cstrong>その人らしさがより表現\u003C/strong>されます。\u003Cbr>\u003Cbr>\u003C/p>\u003Cfigure>\u003Cimg src=\"https://images.microcms-assets.io/assets/0eb919b93c74437dbd287ab903d9ea2c/6caf6947e3ae4a029e92fe92d014ff98/%E3%83%8A%E3%83%81%E3%83%A5%E3%83%A9%E3%83%AB%C3%97%E3%82%AF%E3%83%AA%E3%82%A8%E3%82%A4%E3%83%86%E3%82%A3%E3%83%96.png?w=600&amp;h=749\" alt=\"ナチュラル×クリエイティブ\" width=\"600\" height=\"749\">\u003Cfigcaption>ナチュラルをベースに、色や小物でクリエイティブの要素を足したイメージ。\u003C/figcaption>\u003C/figure>\u003Cp>\u003Cbr>ほかにも、ナチュラル×エレガントであれば、差し色やエレガントな雰囲気のスカーフ、きれいめな形のスラックスなどを取り入れる、といった組み合わせ方もあります。\u003C/p>\u003Ch2 id=\"he1e7cbfa85\">受診しようと思った理由\u003C/h2>\u003Cp>これまでにも、パーソナルカラー診断、骨格診断、顔タイプ診断などを受けており、それぞれ次のような診断結果が出ていました。\u003C/p>\u003Cul>\u003Cli>\u003Cstrong>パーソナルカラー診断\u003C/strong>\u003Cul>\u003Cli>暗い・濃い・深い色が似合う\u003C/li>\u003C/ul>\u003C/li>\u003Cli>\u003Cstrong>骨格診断\u003C/strong>\u003Cul>\u003Cli>ジャストサイズ、下重心、縦ラインを意識した服が似合う\u003C/li>\u003C/ul>\u003C/li>\u003Cli>\u003Cstrong>顔タイプ診断\u003C/strong>\u003Cul>\u003Cli>個性的なデザインが似合う\u003C/li>\u003C/ul>\u003C/li>\u003C/ul>\u003Cp>ただ、これらは\u003Cstrong>あくまで「色」や「形」など、アイテムを構成する要素の話\u003C/strong>でもあります。\u003C/p>\u003Cp>色は濃くて、形はジャストサイズで、デザインは個性的。\u003Cbr>そう言われても、結局どんな服を選べばいいのかよくわかりません。\u003C/p>\u003Cp>それぞれの診断結果をどう組み合わせて、\u003Cstrong>最終的にどんなファッションに着地させる\u003C/strong>とよいのか。その部分を一度プロに見てもらいたいと思い、今回パリ流ファッションスタイル診断を受けてみることにしました。\u003C/p>\u003Ch2 id=\"he437eb6794\">診断を受けるまで\u003C/h2>\u003Ch3 id=\"hcd77833659\">サロン選び\u003C/h3>\u003Cp>絶対に対面で受けたかったので、まずは東京で対面診断をしているサロンを探しました。\u003Cbr>\u003Cbr>ただ、パリ流ファッションスタイル診断はそもそも\u003Cstrong>診断士が少なく\u003C/strong>、目を付けていたサロンもなぜかその時期は軒並み休業中。\u003Cstrong>サロン探しは思ったより難航\u003C/strong>しました。\u003Cbr>\u003Cbr>創始者のトモミさんはパリを拠点にしているため、対面で受けるには現地まで行く必要があります。国内にも診断を行っているサロンはありますが、パリ流を受ける前に、同じサロンでパーソナルカラー診断・骨格診断・顔タイプ診断をすべて受けてからという条件のところがあるなど、\u003Cstrong>時間や費用の面でもなかなかハードルが高め\u003C/strong>でした。\u003Cbr>\u003Cbr>最終的には、対面診断に対応していて、SNS の雰囲気からも話しやすそうだと感じた静岡の診断士の方にお願いすることに。東京出張も可能とのことだったので、\u003Cstrong>東京での出張診断を依頼\u003C/strong>しました。\u003Cbr>\u003Cbr>系統が違いそうな友人と比べながら受けたら楽しそうだったので、\u003Cstrong>ペア診断\u003C/strong>にしました。\u003C/p>\u003Cp>料金は出張費込みで、\u003Cstrong>1 人あたり27,000円\u003C/strong>。大体相場ぐらいの金額です。\u003C/p>\u003Ch3 id=\"h31fc5cbc4a\">事前準備\u003C/h3>\u003Cp>診断前のやり取りは、LINEで行いました。\u003C/p>\u003Cp>事前に送られてきたカウンセリングシートには、年齢や身長、過去に受けたイメージコンサルティング診断の結果、さらに好きなブランドや有名人などのファッションに関する項目に加えて、「自分の性格を一言で表すなら？」「服を着るときに重要視していることは？」といった\u003Cstrong>内面に関する質問\u003C/strong>もありました。\u003Cbr>\u003Cbr>そのほかに、\u003Cstrong>スタイル分析質問票という独自のアンケート\u003C/strong>にも回答。内容としては、少し MBTI に近いような印象です。\u003C/p>\u003Cp>あわせて、\u003Cstrong>普段のコーディネート写真\u003C/strong>も送ります。普段着ている服の好みや雰囲気を見てもらうためのもので、\u003Cstrong>5 枚以上が目安\u003C/strong>でした。\u003Cbr>私は診断のために写真を撮り溜めていたので、40枚くらい送りました。似合っていると思うものだけでなく、いまいちだと思う日の写真も、参考になりそうだったので混ぜています。\u003C/p>\u003Ch2 id=\"hfc6841db05\">診断当日の流れ\u003C/h2>\u003Ch3 id=\"h166a98441e\">当日に行ったこと\u003C/h3>\u003Cp>診断当日は、民家のような雰囲気の広めのレンタルスペースで行われました。\u003Cbr>当日の流れは、大きく分けると次のような感じです。\u003C/p>\u003Cul>\u003Cli>\u003Cstrong>カウンセリングシートをもとにヒアリング\u003C/strong>\u003Cul>\u003Cli>悩みや好みを改めて確認したり、送っていたコーディネート写真についてコメントをもらったりしました。\u003C/li>\u003C/ul>\u003C/li>\u003Cli>\u003Cstrong>骨格チェック\u003C/strong>\u003Cul>\u003Cli>鏡を見ながら、肩まわり、腰まわり、膝などを見てもらい、その人の骨格の特徴を確認していきます。\u003C/li>\u003C/ul>\u003C/li>\u003Cli>\u003Cstrong>アイテムを合わせてチェック\u003C/strong>\u003Cul>\u003Cli>診断士が持参した革ジャン、ジャージ、カーディガンなど、系統の違うアイテムを6種類ほど着て、似合い方を見てもらいました。\u003Cstrong>生地の軽さや柔らかさ、色、襟や袖の形、装飾品の要不要\u003C/strong>などを見ていきます。\u003C/li>\u003C/ul>\u003C/li>\u003C/ul>\u003Cp>印象的だったのはトレンチコートです。友人が着たときに「これは少しうるさすぎる」と言われ、診断士が襟や肩章をどんどん隠していきました。\u003Cstrong>最終的にはボタンなども含めてほとんど隠された状態になったのですが、その状態が一番しっくりきていて驚きました。\u003C/strong>\u003C/p>\u003Ch3 id=\"h6db3938d18\">診断結果の決まり方\u003C/h3>\u003Cp>ここまでのチェックが終わると、いよいよ診断結果を出していきます。\u003C/p>\u003Cp>ただし、「あなたはこれです」といきなり言われるわけではありません。\u003Cstrong>先ほど紹介した10タイプのスタイルを、遠いものから順番に落としていく形\u003C/strong>で診断が進みます。\u003C/p>\u003Cp>落としたスタイルについては、その都度理由を説明してくれます。「これは少し軽すぎる」「遊び心が足りない」といった感じで解説されるのですが、\u003Cstrong>中でも面白かったのが「意図せぬ生活感が出る」という表現\u003C/strong>でした。聞いていると、たしかにそうかもと納得できる部分が多かったです。\u003C/p>\u003Cp>\u003Cstrong>最後に残ったものが、メインのスタイル\u003C/strong>になります。\u003C/p>\u003Cp>メインが決まったら、次にサブとアクセントを見ていきます。ただし、\u003Cstrong>サブとアクセントは、単純に「最後まで残った順番」で決まるわけではありません\u003C/strong>。人によっては、もっと早い段階で落としたスタイルから要素を持ってくることもあるそうです。\u003C/p>\u003Cp>このあたりは、\u003Cstrong>全体のバランスを見ながら決めていく\u003C/strong>とのことでした。また、2つのスタイルで迷う場合は、事前に回答したスタイル分析質問票の内容など、\u003Cstrong>内面の傾向を考慮することもある\u003C/strong>そうです。\u003C/p>\u003Cp>こうして、私の診断結果も出してもらいました。\u003C/p>\u003Ch2 id=\"h0dbced7af6\">診断結果\u003C/h2>\u003Cp>診断の結果、私のスタイルは次のようになりました。\u003C/p>\u003Cul>\u003Cli>\u003Cstrong>メイン：クリエイティブ\u003C/strong>\u003Cul>\u003Cli>特定のスタイルに縛られない、自由度の高さを活かす。\u003C/li>\u003C/ul>\u003C/li>\u003Cli>\u003Cstrong>サブ：セクシー\u003C/strong>\u003Cul>\u003Cli>濃厚さや重さを足す。\u003C/li>\u003C/ul>\u003C/li>\u003Cli>\u003Cstrong>アクセント：シック\u003C/strong>\u003Cul>\u003Cli>好みとしても取り入れたい「強さ」を補う。\u003C/li>\u003C/ul>\u003C/li>\u003C/ul>\u003Cfigure>\u003Cimg src=\"https://images.microcms-assets.io/assets/0eb919b93c74437dbd287ab903d9ea2c/3568708393cd47ffa31ef59d7580ec36/%E8%A8%BA%E6%96%AD%E7%B5%90%E6%9E%9C-1.png?w=600&amp;h=749\" alt=\"クリエイティブ、セクシー、シックをイメージしたコーディネート例\" width=\"600\" height=\"749\">\u003Cfigcaption>クリエイティブ、セクシー、シックをイメージしたコーディネート例。\u003C/figcaption>\u003C/figure>\u003Cp>\u003Cbr>キーワードとして言われたのは、\u003Cstrong>遊び心、動的エネルギー、濃厚さ、重さ\u003C/strong>です。\u003C/p>\u003Cp>10タイプの中で近いもの・遠いものを整理すると、だいたい次のような位置づけでした。\u003C/p>\u003Cul>\u003Cli>近い：クリエイティブ / セクシー / シック / ドラマティック / スポーティ\u003C/li>\u003Cli>中間：トラディショナル / ナチュラル / クラシック\u003C/li>\u003Cli>遠い：ロマンティック / エレガント\u003C/li>\u003C/ul>\u003Cp>カチッとしたものや軽さのあるものだと、私の中にある動的エネルギーを弱めてしまうそうです。一方で、スポーティ以降はどれもいけると言われていて、特定のスタイルに絞るよりも、\u003Cstrong>自由度の高さを活かす方向\u003C/strong>でメインはクリエイティブになりました。\u003Cbr>\u003Cbr>ちなみに、\u003Cstrong>一緒に受けた友人は私とかなり真逆の結果\u003C/strong>でした。\u003C/p>\u003Cul>\u003Cli>\u003Cstrong>メイン：エレガント\u003C/strong>\u003Cul>\u003Cli>上品さ、調和、清潔感を土台にする。\u003C/li>\u003C/ul>\u003C/li>\u003Cli>\u003Cstrong>サブ：ロマンティック\u003C/strong>\u003Cul>\u003Cli>パーソナルカラーや顔タイプから、フェミニンな要素を補う。\u003C/li>\u003C/ul>\u003C/li>\u003Cli>\u003Cstrong>アクセント：ナチュラル\u003C/strong>\u003Cul>\u003Cli>抜け感や軽やかさ、安心感を足す。\u003C/li>\u003C/ul>\u003C/li>\u003C/ul>\u003Cfigure>\u003Cimg src=\"https://images.microcms-assets.io/assets/0eb919b93c74437dbd287ab903d9ea2c/2db774a681334dfdbdf05cd06734e4ca/%E8%A8%BA%E6%96%AD%E7%B5%90%E6%9E%9C-2.png?w=600&amp;h=749\" alt=\"エレガント、ロマンティック、ナチュラルをイメージしたコーディネート例\" width=\"600\" height=\"749\">\u003Cfigcaption>エレガント、ロマンティック、ナチュラルをイメージしたコーディネート例。\u003C/figcaption>\u003C/figure>\u003Cp>友人のキーワードは、\u003Cstrong>清潔感、上品さ、調和\u003C/strong>。そのど真ん中にあるスタイルとして、メインはエレガントになりました。\u003C/p>\u003Cul>\u003Cli>近い：エレガント / ロマンティック / ナチュラル\u003C/li>\u003Cli>中間：クラシック / トラディショナル / シック / クリエイティブ\u003C/li>\u003Cli>遠い：セクシー / スポーティ / ドラマティック\u003C/li>\u003C/ul>\u003Cp>遊びや違和感など、調和を乱すような要素はあまり入れない方がいい一方で、パーソナルカラーや顔の雰囲気にはフェミニンな要素もあるため、それを補うものとしてサブにロマンティックが入っています。\u003C/p>\u003Cp>今回は友人とかなり違う方向性の結果になったので、比べながら聞けたのも面白かったです。\u003C/p>\u003Ch2 id=\"h97dbccb5aa\">受けてみた感想\u003C/h2>\u003Cp>今回の診断は、自分で「この服いい感じかも」と思っていたものに、\u003Cstrong>太鼓判を押してもらったような体験\u003C/strong>でした。\u003C/p>\u003Cp>好きな服や、なんとなくしっくりくるファッションの系統を、要素に分解して言語化してもらえたことで、\u003Cstrong>「好きな感じ」「なんかいい感じ」を前より目指しやすくなった\u003C/strong>気がします。\u003C/p>\u003Cp>ただ、「これを着れば正解です」と答えを決めてもらえる診断ではありません。教えてもらった人やアイテムのすべてにぴんと来たわけではないので、診断結果をヒントにしながら、最終的には自分で模索していくものなのだと思います。\u003C/p>\u003Cp>それでも、今日の服が少しいまいちかもと思ったときに、どこを調整すればよさそうか考えやすくなりました。自分の雰囲気をポジティブな言葉で表現してもらえるのも、普通に気分が上がります。\u003C/p>\u003Cp>否定されたり、苦手なものを押し付けられたりすることもなく、\u003Cstrong>安心して受けられる診断\u003C/strong>でした。\u003Cbr>パリ流ファッションスタイル診断に興味を持った方は、ぜひ一度受けてみてください。\u003C/p>",172,0,3,{"contents":191,"totalCount":401,"offset":188,"limit":402},[192,194,197,200,203,206,209,212,215,218,221,224,227,230,233,236,238,241,244,245,248,251,254,257,260,263,266,269,272,275,278,281,284,286,288,291,294,297,299,302,305,308,310,313,316,319,322,324,327,330,333,334,337,340,341,344,346,349,351,354,357,360,361,364,367,370,373,376,379,382,385,388,390,392,395,398],{"id":152,"name":193},"AI",{"id":195,"name":196},"workshop","ワークショップ",{"id":198,"name":199},"tool","ツール",{"id":201,"name":202},"office","オフィス",{"id":204,"name":205},"internal-project","社内案件",{"id":207,"name":208},"event","イベント",{"id":210,"name":211},"iot","IoT",{"id":213,"name":214},"art","アート",{"id":216,"name":217},"moving","引越し",{"id":219,"name":220},"picture","映像",{"id":222,"name":223},"plant","植物",{"id":225,"name":226},"company-trip","社員旅行",{"id":228,"name":229},"health","健康",{"id":231,"name":232},"exercise","運動",{"id":234,"name":235},"bicycle-club","自転車部",{"id":52,"name":237},"PR",{"id":239,"name":240},"interview","Interview",{"id":242,"name":243},"recruiting","採用",{"id":31,"name":35},{"id":246,"name":247},"history","歴史",{"id":249,"name":250},"life","生活",{"id":252,"name":253},"club","部活動",{"id":255,"name":256},"character","キャラクター",{"id":258,"name":259},"food","食",{"id":261,"name":262},"math","数学",{"id":264,"name":265},"alcohol","酒",{"id":267,"name":268},"infrastructure","インフラ",{"id":270,"name":271},"application","アプリ",{"id":273,"name":274},"parenting","子育て",{"id":276,"name":277},"data","通信",{"id":279,"name":280},"migration","地方移住",{"id":282,"name":283},"security","セキュリティ",{"id":285,"name":285},"esports",{"id":54,"name":287},"振り返り",{"id":289,"name":290},"animal","動物",{"id":292,"name":293},"css","CSS",{"id":295,"name":296},"game","ゲーム",{"id":154,"name":298},"TypeScript",{"id":300,"name":301},"management","マネジメント",{"id":303,"name":304},"remote-work","リモートワーク",{"id":306,"name":307},"frame-work","フレームワーク",{"id":78,"name":309},"音楽",{"id":311,"name":312},"money","お金",{"id":314,"name":315},"covid-19","コロナ",{"id":317,"name":318},"railway","鉄道",{"id":320,"name":321},"agriculture","農業",{"id":80,"name":323},"趣味",{"id":325,"name":326},"certification","資格",{"id":328,"name":329},"personal-computer","PC",{"id":331,"name":332},"welfare","福利厚生",{"id":27,"name":29},{"id":335,"name":336},"study","勉強",{"id":338,"name":339},"mental-health","メンタルヘルス",{"id":16,"name":19},{"id":342,"name":343},"pilates","ピラティス",{"id":156,"name":345},"バックエンド",{"id":347,"name":348},"tunnel","トンネル",{"id":57,"name":350},"デザイン",{"id":352,"name":353},"sports","スポーツ",{"id":355,"name":356},"online","オンライン",{"id":358,"name":359},"internship","インターンシップ",{"id":22,"name":25},{"id":362,"name":363},"virtual-reality","VR",{"id":365,"name":366},"ssl","SSL",{"id":368,"name":369},"perl","Perl",{"id":371,"name":372},"operating-system","OS",{"id":374,"name":375},"ios","iOS",{"id":377,"name":378},"google","Google",{"id":380,"name":381},"deeplearning","DeepLearning",{"id":383,"name":384},"aws","AWS",{"id":386,"name":387},"apple","Apple",{"id":136,"name":389},"API",{"id":61,"name":391},"3D",{"id":393,"name":394},"school","学校",{"id":396,"name":397},"blender","Blender",{"id":399,"name":400},"android","Android",76,200,{"contents":404,"totalCount":189,"offset":188,"limit":189},[405,457,522],{"id":406,"createdAt":407,"updatedAt":408,"publishedAt":409,"revisedAt":408,"title":410,"category":411,"thumbnail":420,"image":424,"content":431,"keyword":432,"client":447,"team":448,"url":452,"release":453,"status":454,"top":20,"blog":36,"service":456},"phpj-noel-2025","2025-12-15T06:18:04.117Z","2026-06-17T08:10:29.660Z","2026-03-05T04:15:03.177Z","PIERRE HERMÉ PARIS | NOËL 2025",[412,417],{"id":413,"createdAt":414,"updatedAt":415,"publishedAt":414,"revisedAt":414,"name":416},"web","2024-03-22T05:33:05.262Z","2024-06-24T23:16:42.692Z","ウェブ制作",{"id":57,"createdAt":418,"updatedAt":418,"publishedAt":418,"revisedAt":418,"name":419},"2024-03-22T05:34:30.127Z","デザイン＆ディレクション",{"url":421,"height":422,"width":423},"https://images.microcms-assets.io/assets/d6dcb8ec530642e7a9b0d0c0a93bf874/47e7b069013a45b8b6e931ec132bd272/thumb.png",900,1200,[425,427,429],{"url":426,"height":48,"width":49},"https://images.microcms-assets.io/assets/d6dcb8ec530642e7a9b0d0c0a93bf874/43a703ebe8d7467aac2c995378cb081e/1.png",{"url":428,"height":48,"width":49},"https://images.microcms-assets.io/assets/d6dcb8ec530642e7a9b0d0c0a93bf874/6aed2d670822422696329a5cb3a6877f/2.png",{"url":430,"height":48,"width":49},"https://images.microcms-assets.io/assets/d6dcb8ec530642e7a9b0d0c0a93bf874/b691d73151a94dcf9d60a6f8af436eac/3.png","\u003Cp>昨年に続き、ホリデーシーズン限定の『NOËL 2025』スペシャルサイトを、\u003Cstrong>デザインとフロントエンドで担当\u003C/strong>しました。\u003Cbr>\u003Cbr>\u003Cstrong>「深海(アビス)」をテーマ\u003C/strong>に、鮮やかな青から徐々に暗く沈んでいくグラデーションで、海の中を潜っていくような体験を表現。泡や珊瑚、砂などのモチーフを取り入れ、ページ全体で海中の世界を感じられる構成に仕上げました。\u003Cbr>\u003Cbr>砂や泡の表現にはWebGLとGPGPUを用い、数万単位のパーティクルをGPUで並列処理することで滑らかな動作を実現。一部テキストは3D描画で演出しながら、DOM上にも同内容を配置し、表現とSEOの両立を図りました。\u003Cbr>また、Safariなどブラウザ差異による負荷を考慮し、SVGからCanvasへ置き換えるなど最適化を実施。視覚的な印象を保ちながら、各環境で安定して動作するよう設計しています。\u003Cbr>\u003Cbr>\u003Cstrong>デザインと実装の両面から、制作の背景やポイントについてブログで詳しく解説\u003C/strong>しています。\u003C/p>",[433,437,442],{"id":434,"createdAt":435,"updatedAt":436,"publishedAt":435,"revisedAt":436,"name":19,"filter":36,"page":36},"ice0h9h9ap","2024-06-28T01:14:44.830Z","2025-01-15T07:40:01.729Z",{"id":438,"createdAt":439,"updatedAt":440,"publishedAt":439,"revisedAt":440,"name":441,"filter":36,"page":36},"6ncqc37v7nzq","2024-06-28T01:14:12.293Z","2025-01-15T07:39:08.437Z","デザインプロジェクト",{"id":443,"createdAt":444,"updatedAt":445,"publishedAt":444,"revisedAt":445,"name":446,"filter":36,"page":36},"ztrz9np70v","2024-06-28T01:14:41.050Z","2025-01-15T07:40:30.428Z","コンセプトメイキング"," PH PARIS JAPON 株式会社",[449],{"fieldId":450,"title":451},"teamField","S2ファクトリー","https://www.s2factory.co.jp/blog/noel2025/","2025.10.17",[455],"公開中",[],{"id":458,"createdAt":459,"updatedAt":460,"publishedAt":459,"revisedAt":460,"title":461,"category":462,"thumbnail":469,"image":473,"content":478,"keyword":479,"client":461,"team":494,"url":517,"release":518,"status":519,"statusText":520,"top":36,"blog":36,"service":521},"tamabi","2024-06-28T01:58:38.788Z","2025-05-27T03:25:42.261Z","多摩美術大学",[463,464],{"id":413,"createdAt":414,"updatedAt":415,"publishedAt":414,"revisedAt":414,"name":416},{"id":465,"createdAt":466,"updatedAt":467,"publishedAt":466,"revisedAt":466,"name":468},"system","2024-03-22T05:33:52.708Z","2024-06-24T23:16:53.739Z","システム開発",{"url":470,"height":471,"width":472},"https://images.microcms-assets.io/assets/d6dcb8ec530642e7a9b0d0c0a93bf874/dea8e004e627415c80bf7b8d622bb4b9/tamabi-thumbnail-1.png",456,600,[474],{"url":475,"height":476,"width":477},"https://images.microcms-assets.io/assets/d6dcb8ec530642e7a9b0d0c0a93bf874/a000f3cedf5d474d83c5b70e8c11db4e/tamabi-other-1.png",1305,1740,"\u003Cp>「多摩美術大学」ウェブサイトリニューアルのお手伝いをさせていただきました。\u003Cbr>\u003Cbr>サイトのコンテンツを大幅に整理し、運用効率を考慮した再利用しやすく組み合わせやすいUIコンポーネントを構築。また多くの方に利用していただけるようにアクセシビリティの向上にも注力しました。\u003Cbr>インフラは、従来のオンプレミスとクラウドが混在していた環境から、AWS への段階的な移行を進めており、今後の完全移行に向けた準備も進行中です。 \u003Cbr>\u003Cbr>弊社は、フロントエンドとバックエンドの実装、インフラ構築を担当しました。\u003C/p>",[480,484,488,493],{"id":481,"createdAt":482,"updatedAt":483,"publishedAt":482,"revisedAt":483,"name":384,"filter":36,"page":36},"uftrxsmd0v72","2024-06-28T01:14:13.671Z","2025-01-15T07:39:25.538Z",{"id":485,"createdAt":486,"updatedAt":486,"publishedAt":486,"revisedAt":486,"name":487,"filter":20,"page":20},"yctx9hzd4","2024-06-28T01:14:33.743Z","Lambda",{"id":489,"createdAt":490,"updatedAt":491,"publishedAt":490,"revisedAt":491,"name":492,"filter":36,"page":36},"u84cy0fbzno","2024-06-28T01:14:39.521Z","2025-01-15T07:40:41.433Z","Cloud",{"id":434,"createdAt":435,"updatedAt":436,"publishedAt":435,"revisedAt":436,"name":19,"filter":36,"page":36},[495,498,501,504,507,510,513,516],{"fieldId":450,"title":496,"link":497},"萩原 俊矢","https://shunyahagiwara.com/#/works",{"fieldId":450,"title":499,"link":500},"GrandBase Inc.","https://www.grandbase.jp/",{"fieldId":450,"title":502,"link":503},"DSCL Inc.","https://dscl.jp/",{"fieldId":450,"title":505,"link":506},"株式会社トラフィックス","https://traffics.jp/",{"fieldId":450,"title":508,"link":509},"株式会社カンバセーションズ","https://qonversations.jp/",{"fieldId":450,"title":511,"link":512},"Measurements Inc.","https://www.measurements.co.jp/",{"fieldId":450,"title":514,"link":515},"tha ltd.","https://tha.jp/",{"fieldId":450,"title":451},"https://www.tamabi.ac.jp/","2024.4.4",[455],null,[416],{"id":523,"createdAt":524,"updatedAt":525,"publishedAt":524,"revisedAt":525,"title":526,"category":527,"thumbnail":529,"image":531,"content":540,"keyword":541,"client":514,"team":557,"url":563,"release":564,"status":565,"statusText":520,"top":20,"blog":36,"service":566},"humanity","2024-06-28T01:56:51.507Z","2025-05-27T03:26:34.287Z","HUMANITY",[528],{"id":465,"createdAt":466,"updatedAt":467,"publishedAt":466,"revisedAt":466,"name":468},{"url":530,"height":471,"width":472},"https://images.microcms-assets.io/assets/d6dcb8ec530642e7a9b0d0c0a93bf874/740adb4e02c54fee97d12025df5f16a0/humanity-thumbnail-1.png",[532,534,536,538],{"url":533,"height":476,"width":477},"https://images.microcms-assets.io/assets/d6dcb8ec530642e7a9b0d0c0a93bf874/2a10b034854d40e789d38ac33e0a3c4e/humanity-other-1.png",{"url":535,"height":476,"width":477},"https://images.microcms-assets.io/assets/d6dcb8ec530642e7a9b0d0c0a93bf874/1091c52a97f745e5a3fc105bd0e542dc/humanity-other-2.png",{"url":537,"height":476,"width":477},"https://images.microcms-assets.io/assets/d6dcb8ec530642e7a9b0d0c0a93bf874/613c8c836fff40119e9f85097262399d/humanity-other-3.png",{"url":539,"height":476,"width":477},"https://images.microcms-assets.io/assets/d6dcb8ec530642e7a9b0d0c0a93bf874/3a9dfb8cce0d4ec887ead3d3998be04c/humanity-other-4.png","\u003Cp>『HUMANITY』は、インターフェース／ビジュアルデザイナーの中村勇吾氏とゲームクリエイター水口哲也氏のタッグによって制作された PlayStation 5(PS VR2対応)、PlayStation 4(PS VR対応)、Steam(PC用VR対応)用アクションパズルゲームです。\u003Cbr>\u003Cbr>プレイヤーは柴犬を操作して、自我を失った人類を導きゴールを目指します。\u003Cbr>メインのストーリーモードには 90 以上のステージを収録。さらに「STAGE CREATOR」では、自分だけのオリジナルステージを作って全世界のプレイヤーにシェアが可能です。また「USER STAGES」では、他のプレイヤーがシェアしたステージが公開され、カスタムのプレイリストや、タグを使用して検索し、お気に入りのステージを発見・チャレンジできます。\u003Cbr>\u003Cbr>弊社はゲームのコミュニティ機能のバックエンドと管理ツールの作成、インフラを担当しました。\u003C/p>",[542,547,552,553],{"id":543,"createdAt":544,"updatedAt":545,"publishedAt":544,"revisedAt":545,"name":546,"filter":36,"page":36},"fg8e4c9nl","2024-06-28T01:14:04.517Z","2025-01-15T07:36:58.970Z","CMS",{"id":548,"createdAt":549,"updatedAt":550,"publishedAt":549,"revisedAt":550,"name":551,"filter":36,"page":36},"p6qgoo2k9vl9","2024-06-28T01:14:06.759Z","2025-01-15T07:37:22.466Z","大規模データ",{"id":489,"createdAt":490,"updatedAt":491,"publishedAt":490,"revisedAt":491,"name":492,"filter":36,"page":36},{"id":554,"createdAt":555,"updatedAt":556,"publishedAt":555,"revisedAt":556,"name":296,"filter":36,"page":36},"pxnv6cy2u","2024-06-28T01:14:45.570Z","2025-01-15T07:39:54.004Z",[558,561,562],{"fieldId":450,"title":559,"link":560},"Enhance","https://enhance-experience.com/ja/",{"fieldId":450,"title":514,"link":515},{"fieldId":450,"title":451},"https://humanity.game/ja/","2023.5.16",[455],[468],1783572974350]