← Semua tutor

Hasil akhir · tanpa menulis efeknya sendiri

Hero polos jadi hidup
dalam beberapa menit.

Sebelum · hero polos

Sesudah · dot grid dari asset.inwebsite.online

Dua web yang sama, bedanya satu snippet. Gerakkan kursor di kartu kanan: titik-titik di belakang hero ikut bereaksi. Di bawah ini caranya dari nol, langkah demi langkah.

Langkah 1 · Siapkan dulu

Yang perlu ada sebelum mulai.

  • Editor kode apa saja. VS Code, Cursor, atau yang kamu biasa pakai.
  • Node.js dan proyek React. Snippet berjalan di React.
  • AI agent (opsional, tapi paling cepat). Di panduan ini contohnya Claude Code.
  • Akun InWebAsset. Mencari dan merencanakan gratis tanpa login, tapi kode hanya bisa diambil setelah login.
Perlu loginTanpa login kamu bisa menjelajah, melihat pratinjau, dan memakai MCP untuk mencari. Tombol Copy kode dan API key MCP baru bisa dipakai setelah Sign in.
Batas harian: 5 salinanAkun gratis boleh menyalin kode snippet 5 kali sehari. Setiap salinan dihitung, termasuk snippet yang pernah kamu salin, dan kuotanya dipakai bersama antara situs dan MCP. Akun Pro tanpa batas. Pilih snippet dengan hati-hati sebelum menyalin.

Mencari, melihat pratinjau, dan perencanaan lewat agent tidak memakai kuota.

Langkah 2 · Jelajahi katalog

Pilih desain yang mau diambil.

Buka asset.inwebsite.online. Katalognya berisi 172 snippet. Persempit lewat kategori, tempat pemakaian, atau kata kunci.

  • Pilih kategori Backgrounds & Ambient.
  • Di Where it goes, pilih Background (atau Hero).
  • Atau ketik di kolom cari, misalnya dot grid.
  • Arahkan kursor ke kartu untuk melihat pratinjau bergerak, lalu klik Dot Grid Spotlight.
REKAMAN · KATALOG
REKAMAN · HALAMAN SNIPPET

Langkah 3 · Buka halaman snippet

Lihat dulu, baru ambil.

  • Tab Preview: gerakkan kursor di pratinjau untuk merasakan efeknya.
  • Tab Code: sebelum login, kodenya terkunci. Muncul pesan Sign in to get the code.
  • Install: npm i gsap @gsap/react. Syaratnya React + Tailwind CSS.
  • When to use: cocok untuk latar hero, belakang form, atau tekstur halaman portofolio.
  • For agents: ada tombol Copy prompt dan tautan registry untuk AI agent.
IngatUntuk menyalin kode kamu harus login, dan akun gratis dibatasi 5 salinan per hari.

Langkah 4 · Ambil kodenya

Dua jalur, pilih satu.

JALUR A · LEBIH CEPATPakai MCP lewat AI agent

Agent mencari, memilih, dan memasang snippet sendiri. Buka halaman For agents, login, buat API key, lalu salin perintahnya.

$claude mcp add --transport http InWebAsset https://asset.inwebsite.online/mcp --header "Authorization: Bearer <API key-mu>"

Perintah di atas untuk Claude Code. Halaman itu juga punya versi Claude Desktop, Cursor, dan VS Code. Jangan pernah membagikan API key-mu.

Mencari dan perencanaan tanpa kunci. Kodenya (get_snippet) baru keluar dengan API key, dan memakai jatah 5 salinan.

JALUR B · MANUALSalin source code

Login, buka halaman snippet, tekan Copy di tab Code. Simpan sebagai komponen, lalu pasang sendiri atau kirim ke agent.

$ npm i gsap @gsap/react

Simpan sebagai components/DotGridSpotlight.tsx. Tiap Copy memakai 1 dari 5 jatah harianmu.

REKAMAN · HALAMAN FOR AGENTS

Langkah 5 · Beri konteks ke agent

Bilang di mana snippet ini ditaruh.

Sebelum mengirim, kasih tahu agent bagian web mana yang mau diubah. Misalnya hero section. Makin jelas konteksnya, makin sedikit salinan yang terbuang dari jatah 5 harianmu.

Bagian web hero sectionFile Hero.jsxStack React + CSS biasaWarna #e8461eBatasan jangan ubah yang lain
Kalau pakai Jalur B: tempel kodenya setelah prompt ini
Ini kode komponen dari asset.inwebsite.online. Taruh sebagai latar belakang hero section di src/sections/Hero.jsx, di belakang judul dan tombol. Proyek saya React + CSS biasa tanpa Tailwind, jadi sesuaikan. Aksen #e8461e.

<tempel kode di sini>
contoh · Claude Code (Jalur A)
# sekali saja: sambungkan MCP
$ claude mcp add --transport http InWebAsset https://asset.inwebsite.online/mcp --header "Authorization: Bearer <API key-mu>"

# masuk ke folder proyek, jalankan agent
$ claude

> Pakai InWebAsset MCP. Ambil snippet backgrounds/dot-grid-spotlight dan pasang sebagai latar belakang hero section di src/sections/Hero.jsx, di belakang judul dan tombol (teks tetap bisa diklik). Proyek saya React + CSS biasa, tanpa Tailwind, jadi sesuaikan kodenya. Warna aksen #e8461e, latar terang #f1ede4. Cek dulu snippet-nya dengan includeCode:false, lalu ambil kodenya sekali saja karena jatah salinanku 5 per hari. Jangan ubah bagian lain.

Yang kamu dapat

Gerakkan kursormu.

Efeknya: titik di dekat kursor membesar, berubah warna, dan condong ke arahnya. Ini versi sederhana supaya hidup di halaman ini. Kode aslinya di kanan.

Setiap titik
merasakanmu.
Gerakkan kursor di sini
gap 22radius 150dotSize 1.4accent #ff5a36
components/DotGridSpotlight.tsx
/*
 * Dot Grid Spotlight
 * A canvas dot grid where dots near the pointer grow, glow in an accent color, and lean toward it like a magnetic field, under a soft spotlight. Works in light and dark mode.
 *
 * id: backgrounds/dot-grid-spotlight · v1.0.0
 * source: https://asset.inwebsite.online/snippets/backgrounds/dot-grid-spotlight
 *
 * Install: npm i gsap @gsap/react
 * Requires: React + Tailwind CSS.
 */
"use client";

import { useRef, type PointerEvent, type ReactNode } from "react";
import gsap from "gsap";
import { useGSAP } from "@gsap/react";

gsap.registerPlugin(useGSAP);

const TAU = Math.PI * 2;

type DotGridSpotlightProps = {
  /** Distance between dots in px. */
  gap?: number;
  /** Dot color. Defaults to the panel's text color (neutral, follows light/dark mode). */
  dotColor?: string;
  /** Highlight color for dots near the pointer and for the spotlight. */
  accent?: string;
  /** Reach of the pointer's pull in px. */
  radius?: number;
  /** Base dot radius in px. */
  dotSize?: number;
  /** A virtual pointer traces a Lissajous loop while the real one is away. */
  autoPlay?: boolean;
  children?: ReactNode;
  className?: string;
};

function SampleContent({ accent }: { accent: string }) {
  return (
    <div className="flex h-full flex-col items-center justify-center px-6 text-center">
      <span className="rounded-full bg-white/80 px-3 py-1 text-xs font-medium text-neutral-600 ring-1 ring-black/5 backdrop-blur dark:bg-neutral-900/80 dark:text-neutral-300 dark:ring-white/10">
        Pointer-reactive background
      </span>
      <h2 className="mt-4 max-w-xl text-4xl leading-[1.05] font-semibold tracking-tight text-neutral-900 sm:text-6xl dark:text-white">
        Every dot feels you <span style={{ color: accent }}>coming.</span>
      </h2>
      <p className="mt-4 max-w-md text-sm text-neutral-600 sm:text-base dark:text-neutral-400">
        A magnetic dot field with a soft spotlight. Move your cursor across it.
      </p>
    </div>
  );
}

export default function DotGridSpotlight({
  gap = 22,
  dotColor,
  accent = "#ff5a36",
  radius = 150,
  dotSize = 1.4,
  autoPlay = false,
  children,
  className = "",
}: DotGridSpotlightProps) {
  const root = useRef<HTMLDivElement>(null);
  const canvas = useRef<HTMLCanvasElement>(null);
  const handlers = useRef<{ move: (x: number, y: number) => void; leave: () => void } | null>(null);

  useGSAP(
    (_context, contextSafe) => {
      const el = root.current;
      const cv = canvas.current;
      const ctx = cv?.getContext("2d");
      const spotEl = el?.querySelector(".dgs-spot");
      if (!el || !cv || !ctx || !spotEl || !contextSafe) return;

      let w = 0;
      let h = 0;
      let dpr = 1;
      let dots: number[] = [];
      const p = { x: 0, y: 0, power: 0 };
      const setSpotX = gsap.quickSetter(spotEl, "x", "px");
      const setSpotY = gsap.quickSetter(spotEl, "y", "px");
      const setSpotO = gsap.quickSetter(spotEl, "opacity");
      let color = "";
      let colorAge = 0;

      const draw = () => {
        // Re-read the (theme-dependent) text color every ~half second.
        if (colorAge-- <= 0) {
          color = dotColor ?? getComputedStyle(cv).color;
          colorAge = 30;
        }
        ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
        ctx.clearRect(0, 0, w, h);
        const lit: number[] = [];
        ctx.fillStyle = color;
        ctx.beginPath();
        for (let i = 0; i < dots.length; i += 2) {
          let x = dots[i];
          let y = dots[i + 1];
          let r = dotSize;
          const dx = p.x - x;
          const dy = p.y - y;
          const d = Math.hypot(dx, dy);
          if (p.power > 0.01 && d < radius) {
            // Smoothstep falloff: dots grow and lean toward the pointer like iron filings.
            let f = 1 - d / radius;
            f = f * f * (3 - 2 * f) * p.power;
            x += dx * f * 0.3;
            y += dy * f * 0.3;
            r += f * dotSize * 2.6;
            lit.push(x, y, r, f);
          }
          ctx.moveTo(x + r, y);
          ctx.arc(x, y, r, 0, TAU);
        }
        ctx.fill();
        ctx.fillStyle = accent;
        for (let i = 0; i < lit.length; i += 4) {
          ctx.globalAlpha = Math.min(1, lit[i + 3] * 1.3);
          ctx.beginPath();
          ctx.arc(lit[i], lit[i + 1], lit[i + 2], 0, TAU);
          ctx.fill();
        }
        ctx.globalAlpha = 1;
        setSpotX(p.x);
        setSpotY(p.y);
        setSpotO(p.power);
      };

      const resize = () => {
        w = el.clientWidth;
        h = el.clientHeight;
        dpr = Math.min(2, window.devicePixelRatio || 1);
        cv.width = Math.round(w * dpr);
        cv.height = Math.round(h * dpr);
        const cols = Math.floor(w / gap);
        const rows = Math.floor(h / gap);
        const ox = (w - cols * gap) / 2;
        const oy = (h - rows * gap) / 2;
        dots = [];
        for (let r = 0; r <= rows; r++) for (let c = 0; c <= cols; c++) dots.push(ox + c * gap, oy + r * gap);
        draw();
      };
      const ro = new ResizeObserver(resize);
      ro.observe(el);
      resize();
      p.x = w / 2;
      p.y = h / 2;

      const mm = gsap.matchMedia();
      mm.add("(prefers-reduced-motion: no-preference)", () => {
        const toX = gsap.quickTo(p, "x", { duration: 0.55, ease: "power3.out" });
        const toY = gsap.quickTo(p, "y", { duration: 0.55, ease: "power3.out" });
        let resume: gsap.core.Tween | null = null;
        let idle: gsap.core.Tween | null = null;

        if (autoPlay) {
          // Virtual pointer on a 3:2 Lissajous curve.
          const lj = { t: 0 };
          gsap.to(p, { power: 1, duration: 0.8, delay: 0.2 });
          idle = gsap.to(lj, {
            t: TAU,
            duration: 10,
            ease: "none",
            repeat: -1,
            onUpdate: () => {
              toX(w / 2 + w * 0.36 * Math.sin(3 * lj.t + Math.PI / 2));
              toY(h / 2 + h * 0.32 * Math.sin(2 * lj.t));
            },
          });
        }

        handlers.current = {
          move: contextSafe((x: number, y: number) => {
            resume?.kill();
            idle?.pause();
            toX(x);
            toY(y);
            gsap.to(p, { power: 1, duration: 0.3, overwrite: "auto" });
          }),
          leave: contextSafe(() => {
            if (idle) resume = gsap.delayedCall(1.2, () => idle?.resume());
            else gsap.to(p, { power: 0, duration: 0.7, ease: "power2.out", overwrite: "auto" });
          }),
        };

        gsap.ticker.add(draw);
        return () => {
          gsap.ticker.remove(draw);
          handlers.current = null;
        };
      });

      return () => ro.disconnect();
    },
    { scope: root, dependencies: [gap, dotColor, accent, radius, dotSize, autoPlay] },
  );

  const onPointerMove = (e: PointerEvent<HTMLDivElement>) => {
    const rect = e.currentTarget.getBoundingClientRect();
    handlers.current?.move(e.clientX - rect.left, e.clientY - rect.top);
  };

  const spot = radius * 2.6;

  return (
    <div
      ref={root}
      onPointerMove={onPointerMove}
      onPointerLeave={() => handlers.current?.leave()}
      className={`relative isolate h-[420px] w-full max-w-5xl overflow-hidden rounded-3xl bg-[#fafaf9] text-neutral-300 ring-1 ring-black/5 dark:bg-[#0b0b0d] dark:text-neutral-700 dark:ring-white/10 ${className}`}
    >
      <div
        aria-hidden
        className="dgs-spot pointer-events-none absolute top-0 left-0 -z-10 rounded-full opacity-0"
        style={{
          width: spot,
          height: spot,
          marginLeft: -spot / 2,
          marginTop: -spot / 2,
          background: `radial-gradient(closest-side, color-mix(in srgb, ${accent} 26%, transparent), transparent)`,
        }}
      />
      <canvas
        ref={canvas}
        aria-hidden
        className="pointer-events-none absolute inset-0 -z-10 size-full [mask-image:radial-gradient(ellipse_at_center,black_55%,transparent_100%)]"
      />
      <div className="relative h-full">{children ?? <SampleContent accent={accent} />}</div>
    </div>
  );
}

Langkah 6 · Bandingkan

Sebelum vs sesudah.

Sebelum

Sesudah

01 · SIAPKANEditor, React, agent, akun (login)
02 · JELAJAHFilter kategori dan placement
03 · BUKAPratinjau, install, kode (login)
04 · AMBILMCP atau salin kode. Maks 5 salinan/hari