jvinhit//lab

Search posts

Type to search across journal entries.

navigate open esc close

SVG from Zero to Senior · Part 13 — Generative Patterns & Backgrounds

Make tiny, resolution-independent textures from a few numbers: repeating <pattern> tiles for hero backgrounds, an organic blob generator with smooth curves, and shipping SVG as a compact data-URI. With a live generator.

A 12 KB hero background PNG, or a 250-byte SVG that’s razor-sharp on every screen and re-themes with one variable? {Một ảnh nền hero PNG 12 KB, hay một SVG 250 byte sắc lẹm trên mọi màn hình và đổi theme bằng một biến?} For dot grids, hatching, subtle textures, and organic blobs, SVG wins by a mile. {Cho lưới chấm, gạch chéo, texture tinh tế, và blob hữu cơ, SVG thắng cách biệt.} This part is the fun one — generating graphics from a handful of numbers. {Phần này là phần vui — sinh đồ hoạ từ một nắm con số.}

Switch patterns, tune the tile, then re-roll the blob generator. {Đổi pattern, chỉnh ô, rồi re-roll máy sinh blob.}

Open the full demo {Mở demo đầy đủ}: /tools/svg-generative-demo/.

Repeating patterns recap → power-up {Ôn pattern lặp → nâng cấp}

In Part 3 you met <pattern>. {Ở Phần 3 con đã gặp <pattern>.} Here’s the production reality: a single tiny tile fills an area of any size, infinitely sharp, for almost no bytes. {Đây là thực tế production: một ô tí hon lấp đầy vùng bất kỳ kích thước, sắc vô hạn, gần như không tốn byte.}

<svg>
  <defs>
    <pattern id="dots" width="22" height="22" patternUnits="userSpaceOnUse">
      <circle cx="11" cy="11" r="3" fill="#c8ff00" />
    </pattern>
  </defs>
  <rect width="100%" height="100%" fill="url(#dots)" />
</svg>

Swap the tile content for the texture you want: {Đổi nội dung ô lấy texture con muốn:}

  • Dots: a <circle>. {một <circle>.}
  • Grid: <path d="M22 0H0V22">. {<path d="M22 0H0V22">.}
  • Diagonal lines: <path d="M0 22L22 0">. {<path d="M0 22L22 0">.}
  • Cross-hatch: both diagonals in one tile. {cả hai đường chéo trong một ô.}

patternUnits="userSpaceOnUse" keeps the tile a fixed size; patternTransform="rotate(30)" rotates the whole texture without redrawing the tile. {patternUnits="userSpaceOnUse" giữ ô kích thước cố định; patternTransform="rotate(30)" xoay cả texture mà không vẽ lại ô.}

Organic blobs — randomness with control {Blob hữu cơ — ngẫu nhiên có kiểm soát}

Those soft “blob” shapes behind modern hero sections look hand-drawn but are pure math. {Các hình “blob” mềm sau các section hero hiện đại trông như vẽ tay nhưng thuần là toán.} The recipe: {Công thức:}

  1. Place n points evenly around a circle (angles 0 → 2π). {Đặt n điểm đều quanh một vòng tròn (góc 0 → 2π).}
  2. Randomize each point’s radius a little — that’s the irregularity. {Random bán kính mỗi điểm một chút — đó là độ bất quy tắc.}
  3. Join them with smooth cubic Béziers through a closed path so there are no corners. {Nối chúng bằng Bézier bậc ba mượt qua một path kín để không có góc.}
function makeBlob(n, irregularity, base = 70, cx = 100, cy = 100) {
  const pts = Array.from({ length: n }, (_, i) => {
    const a = (i / n) * Math.PI * 2;
    const r = base + (Math.random() * 2 - 1) * irregularity;
    return [cx + Math.cos(a) * r, cy + Math.sin(a) * r];
  });
  // Catmull-Rom → cubic control points for a smooth CLOSED path
  let d = `M ${pts[0][0]} ${pts[0][1]}`;
  for (let i = 0; i < n; i++) {
    const p0 = pts[(i - 1 + n) % n], p1 = pts[i],
          p2 = pts[(i + 1) % n], p3 = pts[(i + 2) % n];
    const c1 = [p1[0] + (p2[0] - p0[0]) / 6, p1[1] + (p2[1] - p0[1]) / 6];
    const c2 = [p2[0] - (p3[0] - p1[0]) / 6, p2[1] - (p3[1] - p1[1]) / 6];
    d += ` C ${c1[0]} ${c1[1]}, ${c2[0]} ${c2[1]}, ${p2[0]} ${p2[1]}`;
  }
  return d + ' Z';
}

Fewer points + low irregularity = a calm pebble; more points + high irregularity = a splat. {Ít điểm + bất quy tắc thấp = viên sỏi êm; nhiều điểm + bất quy tắc cao = vệt bắn.} The Catmull-Rom→Bézier conversion (the /6 control points) is the trick that keeps the curve smooth and closed. {Phép chuyển Catmull-Rom→Bézier (control point /6) là mánh giữ đường cong mượt kín.}

Ship it as a data-URI {Ship nó dưới dạng data-URI}

A generated SVG can live entirely inside a CSS background-image — no extra network request, no file: {Một SVG sinh ra có thể sống trọn trong background-image của CSS — không request mạng thêm, không file:}

.hero {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' \
    width='22' height='22'%3E%3Ccircle cx='11' cy='11' r='3' fill='%23c8ff00'/%3E%3C/svg%3E");
}

Rules for a clean data-URI: {Luật cho data-URI sạch:}

  • Use single quotes inside the SVG so you don’t escape the CSS double quotes. {Dùng dấu nháy đơn trong SVG để khỏi escape nháy kép của CSS.}
  • URL-encode # as %23 and </> if needed; prefer URL-encoding over base64 (smaller, still readable). {URL-encode # thành %23</> nếu cần; ưu tiên URL-encode hơn base64.}
  • Keep it tiny — data-URIs aren’t cached separately, so don’t inline a huge illustration this way. {Giữ nó nhỏ — data-URI không cache riêng, nên đừng inline minh hoạ khổng lồ kiểu này.}

Tools like Hero Patterns and SVG Backgrounds are just this technique with a UI. {Các công cụ như Hero PatternsSVG Backgrounds chỉ là kỹ thuật này kèm UI.}

The master’s warnings {Lời cảnh báo của sư phụ}

  • Close blob paths with Z and feed wrap-around control points, or you get a gap or a kink at the seam. {Đóng path blob bằng Z và cấp control point vòng quanh, nếu không sẽ có khe hở hoặc gãy ở mối nối.}
  • userSpaceOnUse vs objectBoundingBox changes whether the tile size is absolute or relative — a classic “my pattern is one giant blur” bug. {userSpaceOnUse vs objectBoundingBox đổi việc kích thước ô là tuyệt đối hay tương đối.}
  • Don’t base64 small SVGs for CSS — URL-encoding is smaller and human-readable. {Đừng base64 SVG nhỏ cho CSS — URL-encode nhỏ hơn và đọc được.}

Practice, or it didn’t happen {Luyện tập, không thì coi như chưa học}

  1. Dot-grid hero {Hero lưới chấm}: build a dot <pattern>, fill a full-bleed rect, then export it as a CSS background-image data-URI. {dựng <pattern> chấm, fill rect tràn viền, rồi export thành data-URI background-image.}
  2. Blob avatar frame {Khung avatar blob}: generate a blob and use it as a clipPath (Part 8!) for a photo. {sinh một blob và dùng làm clipPath cho một ảnh.}
  3. Animated texture {Texture động}: animate patternTransform to slowly drift a diagonal-line background. {animate patternTransform để nền đường chéo trôi chậm.}

What’s next {Phần tiếp theo}

You can now generate textures and shapes programmatically. {Giờ con sinh texture và hình theo lập trình được.} Next we put SVG inside the framework most of you ship in. {Tiếp theo ta đặt SVG vào framework đa số các con dùng.} In Part 14 we cover SVG in React/JSX: inlining vs importing, the JSX attribute gotchas (className, strokeWidth), turning icons into typed components with props, currentColor theming, and rendering data-driven SVG the React way. {Ở Phần 14 ta nói về SVG trong React/JSX: inline vs import, các bẫy thuộc tính JSX (className, strokeWidth), biến icon thành component có kiểu với props, theme currentColor, và render SVG theo dữ liệu kiểu React.}