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
Sponsored
·
Ship Features Fearlessly
Turn features on and off without deploys. Used by thousands of Ruby developers.
→
mottox2
January 19, 2024
Programming
2.8k
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.4k
なぜコピペで使うコンポーネント集を利用するのか?
mottox2
8
7.6k
UIコンポーネントライブラリをうまく使うためにできること / components-with-designer
mottox2
7
4.1k
Figma Plugin公開までの壁を乗り越える
mottox2
3
4.2k
Puppeteerでつくる画像と動画 / images and videos made with puppeteer
mottox2
0
790
手触りのよいウェブを考える / better-mobile-web
mottox2
3
2.1k
組織と権限とSlack App / slack-app-with-roles
mottox2
1
740
SSRを避けるためにやっていること / ssr-alternative
mottox2
9
3.3k
JSXでつくる宣言的UIなプレゼンテーション / jsx-presentation
mottox2
7
34k
Other Decks in Programming
See All in Programming
人間の目はかわらない、だからJPEGは30年もつ
yuzneri
12
19k
TSX の <Hoge<Fuga>> という構文に驚いた話 / tsx-type-argument-syntax
kanaru0928
0
230
S3 を使うアプリケーションをローカル完結で動かすことに全力を注いでみた / Running S3 Apps Offline
contour_gara
0
520
【やさしく解説 設計編・中級 #6】良いアーキテクチャとは ~ 一本の登り道の、行き先 ~
panda728
PRO
0
220
How I Won Prize Money at a Hackathon Using Codex and Symphony Alpha
yasei_no_otoko
0
100
Claude Code全社展開のためにやったことn選~プラグイン302個・コミッター271人を支えるために~
kenchan
5
1.6k
PostgreSQL 18で考えるUUID主キー
kazuhiro1982
0
470
SlackアプリとLambdaの 連携を構築した話
pawn_4_s
1
120
KotlinConf Extended South Korea 2026 Keynote
l2hyunwoo
0
110
torikago - Ruby::Boxで照らすモジュラモノリスの実行境界
se4weed
1
380
ソフトウェアエンジニアにとっての生成AI - 特性を知って使い倒す / generative ai for software enginner
kishida
5
1.8k
メールのエイリアス機能を履き違えない
isshinfunada
0
240
Featured
See All Featured
16th Malabo Montpellier Forum Presentation
akademiya2063
PRO
0
350
StorybookのUI Testing Handbookを読んだ
zakiyama
31
6.9k
Done Done
chrislema
186
16k
Unlocking the hidden potential of vector embeddings in international SEO
frankvandijk
0
890
Easily Structure & Communicate Ideas using Wireframe
afnizarnur
194
17k
Sam Torres - BigQuery for SEOs
techseoconnect
PRO
0
490
Fantastic passwords and where to find them - at NoRuKo
philnash
52
3.8k
Why Mistakes Are the Best Teachers: Turning Failure into a Pathway for Growth
auna
0
210
Leveraging LLMs for student feedback in introductory data science courses - posit::conf(2025)
minecr
1
340
Sharpening the Axe: The Primacy of Toolmaking
bcantrill
46
2.9k
Six Lessons from altMBA
skipperchong
29
4.4k
Highjacked: Video Game Concept Design
rkendrick25
PRO
1
430
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!