<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Creators · AI Creator Digest</title>
<meta name="description" content="Every creator I feature on AI Creator Digest, with a link to their channel and every digest of theirs I've written.">
<meta property="og:site_name" content="AI Creator Digest">
<meta property="og:type" content="website">
<meta name="twitter:card" content="summary_large_image">
<link rel="alternate" type="application/rss+xml" title="AI Creator Digest" href="/feed.xml">
<link rel="icon" href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'%3E%3Crect width='32' height='32' rx='4' fill='%23B5602B'/%3E%3Ctext x='16' y='22' font-family='Arial,sans-serif' font-weight='700' font-size='17' fill='white' text-anchor='middle'%3ED%3C/text%3E%3C/svg%3E">
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="/web/assets/style.css">
<script defer src="/web/assets/app.js"></script>
<!-- Google tag (gtag.js) -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-NJGKWXP83R"></script>
<script>
  window.dataLayer = window.dataLayer || [];
  function gtag(){dataLayer.push(arguments);}
  gtag('js', new Date());

  gtag('config', 'G-NJGKWXP83R');
</script>

<!-- X conversion tracking base code -->
<script>
!function(e,t,n,s,u,a){e.twq||(s=e.twq=function(){s.exe?s.exe.apply(s,arguments):s.queue.push(arguments);
},s.version='1.1',s.queue=[],u=t.createElement(n),u.async=!0,u.src='https://static.ads-twitter.com/uwt.js',
a=t.getElementsByTagName(n)[0],a.parentNode.insertBefore(u,a))}(window,document,'script');
twq('config','oft1f');
</script>
<!-- End X conversion tracking base code -->
</head>
<body>
<a class="skip" href="#main">Skip to content</a>

<div class="wrap">
  <header class="site-header">
    <a href="/" class="wordmark" style="text-decoration:none">
      <span class="title">AI Creator Digest</span>
      <span class="tag">aicreatordigest.com — youtube, read instead of watched</span>
    </a>
    <nav class="site-nav" aria-label="Primary">
      <a href="/">home</a>
      <a href="/playlists">playlists</a>
      <a href="/digests">digests</a>
      <a href="/creators">creators</a>
      <a href="/about">about</a>
      <span class="spacer"></span>
      <span class="issue" id="issue-marker"></span>
    </nav>
    <hr class="divider">
  </header>

  <main id="main">
    <div class="page-head">
      <div class="eyebrow">Creators</div>
      <h1>Every creator</h1>
      <p class="dek">The people doing the actual teaching. Go subscribe to them — I mean it.</p>
    </div>

    <section class="filters" id="filters" hidden>
      <div class="filter-row">
        <input type="search" id="q" class="filter-search" placeholder="Search creators, channels, topics…"
               autocomplete="off" aria-label="Search creators">
        <label class="filter-sort">
          <span>Sort</span>
          <select id="sort" aria-label="Sort creators">
            <option value="name">A–Z</option>
            <option value="digests">Most digests</option>
            <option value="recent">Recently covered</option>
          </select>
        </label>
      </div>
      <div class="chips" id="topic-chips" aria-label="Filter by topic"></div>
      <p class="filter-count" id="filter-count" aria-live="polite"></p>
    </section>

    <section class="band" style="padding-top:0">
      <div class="rows" id="creator-rows"><p class="loading">Loading…</p></div>
    </section>
  </main>

  <footer class="site-footer">
    <span>AI Creator Digest © 2026</span>
    <span class="spacer"></span>
    <a href="/how-we-synthesize">how i synthesize</a>
    <a href="/how-this-works">how this works</a>
    <a href="https://github.com/crgallego/aicreatordigest/issues/new?title=Channel%20suggestion" target="_blank" rel="noopener">submit a channel</a>
    <a href="/feed.xml">rss</a>
  </footer>
</div>

<script>
document.addEventListener("DOMContentLoaded", async () => {
  const A = window.ACD;
  const index = await A.loadIndex();
  A.renderIssueMarker(index);

  const all = Object.values(index.creators);
  const rowsEl = document.getElementById("creator-rows");
  const countEl = document.getElementById("filter-count");

  A.setMeta({
    title: "Creators",
    description: "Every creator I feature on AI Creator Digest, with a link to their channel and every digest of theirs I've written.",
    path: "/creators",
  });

  if (!all.length) {
    rowsEl.innerHTML = A.emptyState(
      "No creators yet",
      "Creator profiles build themselves automatically as I process their videos."
    );
    return;
  }

  // Only offer the controls once there is enough here for them to earn their
  // space. A filter bar over three creators is furniture, not help.
  const topics = A.topicsAcross(all);
  const showFilters = all.length >= 4 || topics.length >= 3;
  document.getElementById("filters").hidden = !showFilters;

  // A topic can arrive as a deep link from a creator profile, so the directory
  // opens already filtered on it rather than dropping the reader at the top.
  const requested = new URLSearchParams(location.search).get("topic") || "";
  const known = topics.some((t) => t.label === requested);
  const state = { q: "", topic: known ? requested : "", sort: "name" };

  if (showFilters) {
    document.getElementById("topic-chips").innerHTML = topics
      .map(
        (t) =>
          `<button class="chip${t.label === state.topic ? " active" : ""}" data-topic="${A.escapeHtml(t.label)}">${A.escapeHtml(t.label)} <span class="n">${t.count}</span></button>`
      )
      .join("");

    document.getElementById("q").addEventListener("input", (e) => {
      state.q = e.target.value.trim().toLowerCase();
      render();
    });
    document.getElementById("sort").addEventListener("change", (e) => {
      state.sort = e.target.value;
      render();
    });
    document.getElementById("topic-chips").addEventListener("click", (e) => {
      const btn = e.target.closest(".chip");
      if (!btn) return;
      // Clicking the active topic clears it, so the filter is always escapable.
      state.topic = state.topic === btn.dataset.topic ? "" : btn.dataset.topic;
      document.querySelectorAll(".chip").forEach((c) =>
        c.classList.toggle("active", c.dataset.topic === state.topic)
      );
      render();
    });
  }

  render();

  function render() {
    const matched = A.browseCreators(all, state);

    rowsEl.innerHTML = matched.length
      ? matched.map(creatorCard).join("")
      : A.emptyState("Nothing matches that", "Try a different search, or clear the topic filter.");

    if (!showFilters) return;
    const bits = [`${matched.length} of ${all.length} creator${all.length === 1 ? "" : "s"}`];
    if (state.topic) bits.push(`in ${state.topic}`);
    countEl.textContent = bits.join(" ");
  }




  function creatorCard(c) {
    const name = c.creatorName || c.channelName || c.slug;
    const sub = A.joinMeta([
      c.channelName && c.channelName !== name ? c.channelName : "",
      A.plural(c.videoCount || 0, "digest", "digests"),
      c.lastPublishedAt ? `last covered ${A.formatDateShort(c.lastPublishedAt)}` : "",
    ]);
    const tags = (c.topics || [])
      .slice(0, 4)
      .map((t) => `<span class="topic-tag">${A.escapeHtml(t.label)}</span>`)
      .join("");

    return `
      <a class="creator-card${c.image ? " has-avatar" : ""}" href="/creator/${encodeURIComponent(c.slug)}">
        ${c.image ? `<img class="avatar" src="${A.escapeHtml(c.image)}" alt="${A.escapeHtml(name)}" loading="lazy" onerror="this.remove()">` : ""}
        <div class="stack">
          <span class="name">${A.escapeHtml(name)}</span>
          <span class="subline">${A.escapeHtml(sub)}</span>
          ${c.bio ? `<p class="bio">${A.escapeHtml(c.bio)}</p>` : ""}
          ${tags ? `<div class="topic-tags">${tags}</div>` : ""}
        </div>
      </a>`;
  }
});
</script>
</body>
</html>
