Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
もう一歩進めたい OG画像の動的生成
Search
mottox2
January 19, 2024
Programming
2.7k
7
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
もう一歩進めたい OG画像の動的生成
mottox2
January 19, 2024
More Decks by mottox2
See All by mottox2
つくり方を変えていく | change-how-we-build
mottox2
2
1.3k
なぜコピペで使うコンポーネント集を利用するのか?
mottox2
8
7.6k
UIコンポーネントライブラリをうまく使うためにできること / components-with-designer
mottox2
7
4k
Figma Plugin公開までの壁を乗り越える
mottox2
3
4.1k
Puppeteerでつくる画像と動画 / images and videos made with puppeteer
mottox2
0
780
手触りのよいウェブを考える / better-mobile-web
mottox2
3
2k
組織と権限とSlack App / slack-app-with-roles
mottox2
1
730
SSRを避けるためにやっていること / ssr-alternative
mottox2
9
3.3k
JSXでつくる宣言的UIなプレゼンテーション / jsx-presentation
mottox2
7
34k
Other Decks in Programming
See All in Programming
Dataformのリポジトリを立ち上げるときにまずやること / dataform-day0-2026
snhryt
0
200
スマートグラスで並列バイブコーディング
hyshu
0
270
任せる範囲はこう広がった / How the Scope of AI Delegation Has Expanded
nrslib
1
220
Agentic UI
manfredsteyer
PRO
0
220
気圧・高度・GPSを記録&可視化するアプリ「Koudo」を作った話
hjmkth
1
330
エンジニア向け会社紹介/Findy Company Profile
findyinc
6
360k
A2UI という光を覗いてみる
satohjohn
1
170
自作OSでスライド発表する
uyuki234
1
3.5k
そのテスト、説明できますか?~LWテスト戦略FW~のご紹介
nakahara
0
190
Vue × Nuxt × Oxc どこまで使える?実運用の現在地
andpad
0
330
過去最大のMCPアップデート! 2026-07-28 RC版の謎に迫る
licux
6
430
LLM本来の能力を解き放つサンドボックス技術とAI民主化への適用
yukukotani
3
4.7k
Featured
See All Featured
Max Prin - Stacking Signals: How International SEO Comes Together (And Falls Apart)
techseoconnect
PRO
0
190
The AI Search Optimization Roadmap by Aleyda Solis
aleyda
1
6k
More Than Pixels: Becoming A User Experience Designer
marktimemedia
3
460
Paper Plane
katiecoart
PRO
1
52k
Keith and Marios Guide to Fast Websites
keithpitt
413
23k
Claude Code のすすめ
schroneko
67
230k
ReactJS: Keep Simple. Everything can be a component!
pedronauck
666
130k
Understanding Cognitive Biases in Performance Measurement
bluesmoon
32
2.9k
How Fast Is Fast Enough? [PerfNow 2025]
tammyeverts
3
630
Writing Fast Ruby
sferik
630
63k
Building Better People: How to give real-time feedback that sticks.
wjessup
370
20k
Why Mistakes Are the Best Teachers: Turning Failure into a Pathway for Growth
auna
0
180
Transcript
#burikaigi_m もう一歩進めたい OG画像の動的生成 BuriKaigi 2024 @mottox2
#burikaigi_m @mottox2 UIデザインとウェブフロントエンド
#burikaigi_m
#burikaigi_m
#burikaigi_m OG画像 E サイトの情報を伝えるためのOpen Graphというプロトコ P E その中の画像をOG画像と呼んでいh E 特にXではtitleやdescriptionより画像の主張が強いの
で、 各サイトやサービスが工夫を凝らしている
#burikaigi_m @vercel/ogとは?
#burikaigi_m @vercel/og ( JSXからsvgを生成するsatoriとsvgからpngが画像に変 換するresvgをVercel上でまとめて使えるようにしたも) ( Next.jsに組み込まれていて簡単に利用できる JSX satori resvg
SVG PNG
#burikaigi_m 例えば < {{ height: , width: , display: ,
flexDirection: , alignItems: , justifyContent: , backgroundColor: , fontSize: , fontWeight: , }} > < {{ margin: }} > < ></ > </ > < {{ marginTop: }}>Hello, World</ > </ > div svg path path svg div div div style width viewBox fill style d style = = = = = = = '100%' '100%' 'flex' 'column' 'center' 'center' '#fff' "75" "0 0 75 65" "#000" '0 75px' "M37.59.25l36.95 64H.64l36.95-64z" 32 600 40
#burikaigi_m 例えば < < > < /> </ > <
/> < > < /> </ > < > < /> </ > < < > < /> </ > < > < /> </ > svg mask rect mask rect rect mask rect mask image mask rect mask mask rect mask width height viewBox xmlns id x y width height fill x y width height fill id x y width height id x y width height fill x y width height href preserveAspectRatio clip-path mas id x y width height fill id x y width height fill = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = "800" "400" "0 0 800 400" "http://w "satori_om-id" "0" "0" "800" "400" "#fff" "0" "0" "800" "400" "#fff" "satori_cp-id-0" "363" "128" "75" "65" "satori_om-id-0" "363" "128" "75" "65" "#fff" "363" "128" "75" "65" "data:image/svg+xml;utf8,%3Csvg fill=%22%23000%22 xmlns=% "none" "url(#satori_cp-id-0)" "satori_om-id-0-0" "363" "128" "0" "65" "#fff" "satori_om-id-1" "304" "233" "193" "39" "#fff" clipPath clipPath
#burikaigi_m 例えば
#burikaigi_m @vercel/ogの嬉しさ ・ しんどさ 世の中の画像生成手法は座標を考えながら使う必要が あるが@vercel/ogではほとんど考えなく て良s 一方、
SatoriはHTMLのサブセッ トであり、 結構できなこと が多い
#burikaigi_m Playground https://satori-playground.vercel.app
#burikaigi_m サポート状況の確認 https://github.com/vercel/satori
#burikaigi_m 無事画像が作れるようになった ただ、 画像を作るだけではつまらない 自動作成された画像感を減らしたい
#burikaigi_m 自動作成されてる感はどこから来るのか? デザイン段階で決まってC 9 画像が浮いていC 9 フォントがデフォルト、 文字詰めが適b 9 Webっぽいグリッドを感じる
#burikaigi_m 標準で使われているフォントを使うと、 ダサさがある d 標準フォントでは太字指定が無視され@ d「」 、 。 などの役物の空きが気になる
#burikaigi_m デフォルト(Roboto) YakuHanJP
#burikaigi_m 標準で使われているフォントを使うと、 ダサさがある おすすめは qracさんが作られている YakuHanJP MergedのSemiboldかBolc Noto
Sans JPに対して、 半角サイズの役物 (句読点と 鉤括弧等の文字) を混ぜたフォント
#burikaigi_m 文字づめがされていない v 文字詰めをやるCSS `font-feature-settings: ‘palt’` はsatoriがサポートしていないため動かないi v → 場合によって代わりに
`letter-spacing: -0.02em` で全体的に詰めておくのもあり こんにちは世界 こんにちは世界 ツメなし ツメあり
#burikaigi_m 画像の使い方 そのまま使うとブログのアイキャッチ感があふれ` B 特定の形にトリミングしてみ` B 上からグラデーションの座布団を引G B ぼかしてみる
#burikaigi_m 視覚調整 Vercel 正しい位置に配置すると目の錯覚で小さく見える 世の中のロゴやフォントにはこの調整が入っている
#burikaigi_m 視覚調整の対応 R ウェブでは保守性のためスキップすることが多3 R 画像を作るならやるべ$ R マイナス側のマージンをつければOK
#burikaigi_m
#burikaigi_m 大切なこと R 何ができて、 何ができないかを知っておくこと ・ 伝えるこT R Web技術で作れることと、 Webのように作ることを混同
しない方がよ1 R レスポンシブとか考えなく てよく て、 特定のサイズに特化し たスタイリングを行えばよい
#burikaigi_m もう一歩、 プログラムで作る意味を見出したい
#burikaigi_m 何ができそうか? S ランダムに背景を選択すF S SVGを利用した動的なビジュアルを作F S 画像の色を考慮したビジュアルを作る
#burikaigi_m KODANSHAtech コーポレートブログの例
#burikaigi_m ランダムな背景を用意する S 真っ先に思いつくであろう活用U S ランダムに背景画像を切り替える実 S 完全ランダムにしてしまうと、 毎回変わってしまうので、 URLに対して一意にするための実装をするとよw
S シードを設定できるランダム関数の実装
#burikaigi_m class constructor = % if <= += return =
* % return - / let = new { ( ) { .seed seed ; ( .seed ) .seed ; } () { .seed .seed ; } () { ( . () ) ; } } rng ( ); console. (rng. ()); SeededRandom next nextFloat next SeededRandom log nextFloat seed this 2147483647 this 0 this 2147483646 this this 16807 2147483647 this 1 2147483646 12345 // 0から1の範囲で乱数を返す // 使用例 // シード値を設定 // 常に同じシード値で同じ乱数列を生成 generated by ChatGPT 同じURLでは同じ乱数が生成されるようにする
#burikaigi_m SVGを利用した動的なビジュアルを作る E vercel/ogはpngを生成する中間状態にSVGを利用して いるので、 SVGの自由度が高めになっていV E SVGを出し分けるJSXを利用して、 (普通なら実現しにく い)
動的なビジュアルを作る
#burikaigi_m
#burikaigi_m SVGでクリエイティブコーディング return ... = / / / = =
= = = = = = < return ... = / / / = = = = = = = < return ... = = / / / = = / / = = / / / < { }> < { blockSize blockSize blockSize b < { / } { / } { / } { < { / } { / } { / } { } </ > // T 270 if (value ) < { }> < { blockSize blockSize blockSize blockSize < { / } { / } { / } { } < { / } { / } { } /> </ > // X if (value ) < { }> < { } { blockSize blockSize blockSize bloc {/* < { } { blockSize blockSize blockSize < { } { blockSize blockSize blockSize bloc </ > // O 2 2 2 2 2 2 2 2 2 0.25 2 2 2 2 2 2 2 2 0.55 2 2 2 2 2 2 2 2 g groupProps path d rect x blockSize y blockSize width blockSize height blockSize rect y blockSize width blockSize height blockSize fill g g groupProps path d rect y blockSize width blockSize height blockSize fill rect width blockSize height blockSize fill g g groupProps path fill d path fill d path fill d g color color color color color color `M0 ${ } L 0 0 A ${ } ${ } 0 0 1 ${ `M${ } 0 L ${ } 0 A ${ } ${ `m${ } ${ } ${ } ${ `M${ } ${ } 0 0h${ }L${ `m${ } ${ } ${ }-${
#burikaigi_m return ... = / / / = = =
= = = = = < return ... = / / / = = = = = = = < return ... = = / / / = = / / = = / / / < { }> < { blockSize blockSize blockSize b < { / } { / } { / } { < { / } { / } { / } { } </ > // T 270 if (value ) < { }> < { blockSize blockSize blockSize blockSize < { / } { / } { / } { } < { / } { / } { } /> </ > // X if (value ) < { }> < { } { blockSize blockSize blockSize bloc {/* < { } { blockSize blockSize blockSize < { } { blockSize blockSize blockSize bloc </ > // O 2 2 2 2 2 2 2 2 2 0.25 2 2 2 2 2 2 2 2 0.55 2 2 2 2 2 2 2 2 g groupProps path d rect x blockSize y blockSize width blockSize height blockSize rect y blockSize width blockSize height blockSize fill g g groupProps path d rect y blockSize width blockSize height blockSize fill rect width blockSize height blockSize fill g g groupProps path fill d path fill d path fill d g color color color color color color `M0 ${ } L 0 0 A ${ } ${ } 0 0 1 ${ `M${ } 0 L ${ } 0 A ${ } ${ `m${ } ${ } ${ } ${ `M${ } ${ } 0 0h${ }L${ `m${ } ${ } ${ }-${ SVGでクリエイティブコーディング h vercel/ogでは画像アウトプット前の中間状態にSVGを 使っているので、 それを想定して書G h これらのパターンをランダムに並べている
#burikaigi_m 画像の色を考慮したビジュアルを作る 富山で食べた寿司がうまかった
#burikaigi_m 画像の色を考慮したビジュアルを作る 富山で食べた寿司がうまかった 色を考慮しない場合
#burikaigi_m 画像の色を考慮したビジュアルを作る 富山で食べた寿司がうまかった 特徴色を取得 輝度を特定の値に変更 グラデーションの値に利用 hsl(34, 28, 57) hsl(34,
28, 30)
#burikaigi_m 画像の色を考慮したビジュアルを作る 画像の各ピクセル情報から、 その画像の特徴的な色を取 り出し、 その色を画像の要素として利用すX 取り出した色をそのまま使うのではなく、 輝度を落として
利用すると使いやすa 画像処理は重いのでEdgeでは動かせず、 従来のサー バーレス上で動作、 または事前に保存しておく必要あり
#burikaigi_m 画像の色を考慮したビジュアルを作る import from import from import from const =
await const = await const = new const = const = new const = const = const = jpeg { FastAverageColor } ; { rgb2hsl, hsl2rgb } (imageUrl) image. () (image) jpeg. (uint8Array) (); [ , , , ] fac. (decoded.data) [ , , ] (r, g, b) (h, s, ) 'jpeg-js' 'fast-average-color' './utils' image imageArray uint8Array decoded fac r g b a h s l themeColor 0.3 fetch arrayBuffer Uint8Array decode FastAverageColor getColorFromArray4 rgb2hsl hsl2rgb
#burikaigi_m まとめ c Web技術で作れることと、 Webのように作ることを混同 しない方がよe c エンジニアリング的な視点とデザイナー的な視点を混ぜ て、 ちょっと凝った画像生成ができるかもしれない
#burikaigi_m Thank you!