A seating chart maker where guests search for their own name

A seating chart is a list of names and tables. The hard part is the hundred people reading it at the same door at the same time.

The RSVPs are in. You have spent two evenings moving names between tables.

Aunt June cannot sit with Uncle Walt. The college friends want to be together. Table 5 has nine people and eight chairs.

Then the day comes, and a hundred guests crowd one board at the door, all reading for one name.

These days people ask ChatGPT, Claude or Gemini for it and get a finished page in one reply. A seating chart maker that draws the tables from your guest list, with a search box so each guest finds their own seat.

Here is one for a wedding. Type a name, like "rosa" or "kim".

Live exampletry it here, then copy the code
Share it as a link
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Find your table</title>
<style>
  * { box-sizing: border-box; }
  body { margin: 0; background: radial-gradient(circle at 50% -10%, #fbe9dc, transparent 55%), #fbf7f1; color: #2f2a26; font-family: system-ui, -apple-system, "Segoe UI", sans-serif; }
  .page { max-width: 680px; margin: 0 auto; padding: 22px 14px 28px; }
  .hero { text-align: center; }
  .k { font: 700 11px system-ui; letter-spacing: .22em; text-transform: uppercase; color: #a8754f; }
  h1 { margin: 6px 0 2px; font: 400 30px/1.15 Georgia, "Times New Roman", serif; }
  h1 em { color: #a8754f; }
  .date { margin: 0 0 16px; font-size: 14px; color: #85786c; }
  .search { position: relative; }
  .search input { width: 100%; padding: 15px 16px 15px 46px; border: 1.5px solid #e4d6c8; border-radius: 16px; background: #fff; font: 17px system-ui; color: #2f2a26; box-shadow: 0 8px 22px rgba(120, 80, 40, .1); outline: none; }
  .search input:focus { border-color: #a8754f; }
  .search svg { position: absolute; left: 16px; top: 50%; transform: translateY(-50%); }
  .hits { display: flex; flex-wrap: wrap; gap: 6px; margin: 10px 0 0; min-height: 4px; }
  .hits button { border: 0; border-radius: 999px; padding: 8px 12px; background: #2f2a26; color: #fff; font: 600 14px system-ui; cursor: pointer; animation: in .25s both; }
  .hits button span { color: #f2c9a4; margin-left: 4px; }
  .hits p { margin: 0; font-size: 14px; color: #85786c; }
  @keyframes in { from { opacity: 0; transform: translateY(4px); } }
  .you { display: none; margin-top: 12px; padding: 14px 16px; border-radius: 18px; background: #2f2a26; color: #fff; animation: in .3s both; }
  .you.show { display: block; }
  .you .big { font: 400 26px/1.1 Georgia, serif; }
  .you .big b { color: #f2c9a4; font-weight: 400; }
  .you .with { margin-top: 6px; font-size: 14px; color: #d9cfc6; line-height: 1.45; }
  .room { margin-top: 18px; padding: 16px 8px 18px; border-radius: 24px; background: #fff; box-shadow: 0 10px 30px rgba(120, 80, 40, .1); }
  .room h2 { margin: 0 0 4px; text-align: center; font: 700 12px system-ui; letter-spacing: .14em; text-transform: uppercase; color: #a8754f; }
  .stage { margin: 6px auto 12px; width: 60%; padding: 6px; border-radius: 10px; background: #f3e8dc; text-align: center; font: 600 12px system-ui; color: #85786c; }
  .tables { display: grid; grid-template-columns: repeat(3, 1fr); gap: 6px 2px; }
  .t { position: relative; aspect-ratio: 1; cursor: pointer; transition: opacity .3s, transform .3s; }
  .t .top { position: absolute; inset: 24%; border-radius: 50%; background: radial-gradient(circle at 35% 30%, #fff, #f6ece2); border: 1.5px solid #e4d6c8; display: grid; place-items: center; text-align: center; transition: box-shadow .3s, border-color .3s; }
  .t .num { font: 400 clamp(17px, 5vw, 24px)/1 Georgia, serif; }
  .t .nm { font: 600 clamp(10px, 2.6vw, 12px) system-ui; color: #85786c; margin-top: 2px; }
  .t .cnt { font: 600 10px system-ui; color: #b3a596; }
  .t .cnt.over { color: #c2412d; }
  .seat { position: absolute; width: 19%; aspect-ratio: 1; margin: -9.5% 0 0 -9.5%; border-radius: 50%; background: #efe3d6; color: #6d5f53; display: grid; place-items: center; font: 700 clamp(9px, 2.4vw, 11px) system-ui; transition: transform .3s, background .3s; }
  .room.focus .t { opacity: .35; }
  .room.focus .t.on { opacity: 1; transform: scale(1.04); }
  .t.on .top { border-color: #a8754f; box-shadow: 0 0 0 6px rgba(168, 117, 79, .15); animation: glow 1.4s ease-in-out infinite; }
  .seat.me { background: #a8754f; color: #fff; transform: scale(1.35); z-index: 1; }
  @keyframes glow { 50% { box-shadow: 0 0 0 12px rgba(168, 117, 79, .06); } }
  .list { margin-top: 18px; padding: 14px; border-radius: 16px; background: rgba(255, 255, 255, .7); font-size: 13px; color: #6d5f53; }
  .list h3 { margin: 0 0 6px; font-size: 12px; letter-spacing: .1em; text-transform: uppercase; color: #a8754f; }
  .list ul { margin: 0 0 12px; padding: 0; list-style: none; columns: 2; column-gap: 18px; line-height: 1.55; }
  .list ul:last-child { margin-bottom: 0; }
  @media (min-width: 560px) { .list ul { columns: 3; } }
</style>
</head>
<body>
<main class="page">
  <section class="hero">
    <div class="k">Welcome to the reception</div>
    <h1>Priya <em>&amp;</em> Sam</h1>
    <p class="date">Saturday, June 13 &middot; The Orchard Barn</p>
    <div class="search">
      <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="#a8754f" stroke-width="2.4" stroke-linecap="round"><circle cx="11" cy="11" r="7"/><path d="M20 20l-4-4"/></svg>
      <input id="q" placeholder="Type your name to find your table" autocomplete="off" aria-label="Your name">
    </div>
  </section>
  <div class="hits" id="hits"></div>
  <div class="you" id="you"></div>

  <section class="room" id="room">
    <h2>Floor plan</h2>
    <div class="stage">Dance floor &amp; head table</div>
    <div class="tables" id="tables"></div>
  </section>

  <section class="list">
    <h3>Tables</h3>
    <ul id="tablenames">
      <li>1: Rosewood</li><li>2: Olive</li><li>3: Juniper</li><li>4: Maple</li><li>5: Willow</li><li>6: Birch</li>
    </ul>
    <h3>Guest list (name, table)</h3>
    <ul id="guests">
      <li>Anita Rao, table 1</li><li>Vikram Rao, table 1</li><li>Leela Rao, table 1</li><li>Dev Rao, table 1</li><li>Meera Iyer, table 1</li><li>Arjun Iyer, table 1</li>
      <li>Grace Bennett, table 2</li><li>Tom Bennett, table 2</li><li>Nora Bennett, table 2</li><li>Paul Haines, table 2</li><li>Ruth Haines, table 2</li><li>Ellie Haines, table 2</li><li>Mark Ortiz, table 2</li>
      <li>Jasmine Cole, table 3</li><li>Owen Park, table 3</li><li>Kiara Lopez, table 3</li><li>Ben Adler, table 3</li><li>Hana Sato, table 3</li><li>Luis Romero, table 3</li><li>Tess Walker, table 3</li><li>Ade Bello, table 3</li>
      <li>Chris Moreau, table 4</li><li>Ivy Moreau, table 4</li><li>Sanjay Patel, table 4</li><li>Rita Patel, table 4</li><li>Daniel Kim, table 4</li><li>Lucy Kim, table 4</li>
      <li>Fatima Noor, table 5</li><li>Ollie Grant, table 5</li><li>Mia Grant, table 5</li><li>Jonah Fields, table 5</li><li>Zoe Fields, table 5</li><li>Ravi Menon, table 5</li><li>Ana Silva, table 5</li>
      <li>Rosa Alvarez, table 6</li><li>Miguel Alvarez, table 6</li><li>Helen Shaw, table 6</li><li>George Shaw, table 6</li><li>Ivan Petrov, table 6</li><li>Clara Petrov, table 6</li>
    </ul>
  </section>
</main>
<script>
  const SEATS = 8;
  const plain = (s) => s.normalize('NFD').replace(/[\u0300-\u036f]/g, '').toLowerCase();
  const q = document.getElementById('q'), hits = document.getElementById('hits'), you = document.getElementById('you'), room = document.getElementById('room');
  let guests = [], tables = [];

  function read() {
    // Everything comes from the two lists at the bottom of the page.
    tables = [...document.querySelectorAll('#tablenames li')].map((li) => {
      const m = li.textContent.trim().match(/^(\d+)\s*[:.-]?\s*(.*)$/);
      return m ? { n: +m[1], name: m[2] } : null;
    }).filter(Boolean);
    guests = [...document.querySelectorAll('#guests li')].map((li) => {
      const m = li.textContent.trim().match(/^(.+?),\s*table\s*(\d+)\s*$/i);
      return m ? { name: m[1].trim(), t: +m[2] } : null;
    }).filter(Boolean);
  }

  function draw() {
    const box = document.getElementById('tables');
    box.innerHTML = '';
    tables.forEach((tb) => {
      const at = guests.filter((g) => g.t === tb.n);
      const el = document.createElement('div');
      el.className = 't'; el.dataset.n = tb.n;
      const over = at.length > SEATS;
      el.innerHTML = '<div class="top"><div><div class="num">' + tb.n + '</div><div class="nm">' + esc(tb.name) + '</div><div class="cnt' + (over ? ' over' : '') + '">' + at.length + ' / ' + SEATS + '</div></div></div>';
      const total = Math.max(SEATS, at.length);
      for (let i = 0; i < total; i++) {
        const a = (i / total) * Math.PI * 2 - Math.PI / 2;
        const s = document.createElement('div');
        s.className = 'seat';
        s.style.left = 50 + Math.cos(a) * 39 + '%';
        s.style.top = 50 + Math.sin(a) * 39 + '%';
        if (at[i]) { s.textContent = at[i].name.split(/\s+/).map((w) => w[0]).join('').slice(0, 2); s.title = at[i].name; s.dataset.who = at[i].name; }
        else { s.style.background = 'transparent'; s.style.border = '1.5px dashed #e4d6c8'; }
        el.append(s);
      }
      el.onclick = () => focusTable(tb.n, null);
      box.append(el);
    });
  }

  function focusTable(n, who) {
    room.classList.add('focus');
    document.querySelectorAll('.t').forEach((t) => t.classList.toggle('on', +t.dataset.n === n));
    document.querySelectorAll('.seat').forEach((s) => s.classList.toggle('me', !!who && s.dataset.who === who));
    const tb = tables.find((t) => t.n === n) || { name: '' };
    const at = guests.filter((g) => g.t === n).map((g) => g.name);
    if (who) {
      const others = at.filter((x) => x !== who).map((x) => x.split(' ')[0]);
      const withText = others.length > 1 ? others.slice(0, -1).join(', ') + ' and ' + others[others.length - 1] : others.join('');
      you.innerHTML = '<div class="big">' + esc(who.split(' ')[0]) + ', you are at <b>Table ' + n + '</b></div><div class="with">' + esc(tb.name) + ' table. With ' + esc(withText || 'no one else yet') + '.</div>';
    } else {
      you.innerHTML = '<div class="big"><b>Table ' + n + '</b> &middot; ' + esc(tb.name) + '</div><div class="with">' + esc(at.join(', ') || 'No one yet') + '</div>';
    }
    you.classList.add('show');
    document.querySelector('.t.on').scrollIntoView({ behavior: 'smooth', block: 'center' });
  }

  function search() {
    const v = plain(q.value.trim());
    hits.innerHTML = '';
    if (v.length < 2) { room.classList.remove('focus'); document.querySelectorAll('.t').forEach((t) => t.classList.remove('on')); you.classList.remove('show'); return; }
    const found = guests.filter((g) => plain(g.name).split(/\s+/).some((w) => w.startsWith(v)) || plain(g.name).startsWith(v)).slice(0, 8);
    if (!found.length) { hits.innerHTML = '<p>No name starts with that. Try your first name.</p>'; return; }
    found.forEach((g) => {
      const b = document.createElement('button');
      b.innerHTML = esc(g.name) + '<span>Table ' + g.t + '</span>';
      b.onclick = () => focusTable(g.t, g.name);
      hits.append(b);
    });
    if (found.length === 1) focusTable(found[0].t, found[0].name);
  }
  function esc(s) { return s.replace(/[&<>"]/g, (ch) => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;' }[ch])); }

  q.addEventListener('input', search);
  read(); draw();
</script>
</body>
</html>
A wedding seating chart: type a name to see your table, who you sit with, and where the table is on the floor plan. Tap a table to see everyone at it. All names are made up. Edit the code and the example reruns.

Three things that make a seating chart easy to use

1. Search beats scanning. On a board, every guest reads every table to find one line. With a search box, they type four letters and the page shows one answer. Ask for it to match first names and last names.

2. Show who they sit with. "Table 6" is an answer. "Table 6, with Miguel, Helen and Clara" is a reason to smile on the way in. It also helps you spot a mistake before the guests do.

3. Count every table. A drawn table with eight chairs makes the ninth guest obvious. Ask the page to show how many people sit at each table and to flag one that is over.

A board with every name, read by a crowd at once, next to a phone where one guest typed four letters and got their table.
A board with every name, read by a crowd at once, next to a phone where one guest typed four letters and got their table.

Make it with AI

Open ChatGPT, Claude or Gemini and paste this. Change the words in capitals and paste your list at the end.

Make one complete HTML file: a seating chart maker.
Event: NAMES OR EVENT, DATE, PLACE
Tables: NUMBER OF TABLES, SEATS PER TABLE.
Draw the room from above, each table a circle with
its number, its name and its seats around it.
A search box at the top: as a guest types their
first or last name, show their table, highlight it
on the plan, and list who else sits there.
Show each table's count, red if it is over.
Show the table names and the guest list as plain
lists at the bottom, one line per guest written
"Name, table N", and read everything from those lists.
Rules: all CSS and JavaScript inside the file, no images
or fonts from other sites, nothing sent anywhere,
works on a phone. Use only my names, do not invent any.
Table names: PASTE ONE PER LINE
Guest list: PASTE "NAME, TABLE N" ONE PER LINE

"Read everything from those lists" matters most. It keeps every name and table number as plain text on the page, so moving a guest later is a one-character edit, not a new reply.

The same seating chart maker idea works in a classroom. This one seats a class from a name list. Tap a desk to keep that student in place, then shuffle the rest.

Live exampletry it here, then copy the code
Share it as a link
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Classroom seating chart</title>
<style>
  * { box-sizing: border-box; }
  body { margin: 0; background: #eef3f8; color: #1f2a37; font-family: system-ui, -apple-system, "Segoe UI", sans-serif; }
  .page { max-width: 680px; margin: 0 auto; padding: 20px 12px 26px; }
  .k { font: 700 11px system-ui; letter-spacing: .18em; text-transform: uppercase; color: #3a6fd8; }
  h1 { margin: 4px 0 4px; font-size: 24px; }
  .sub { margin: 0 0 12px; font-size: 14px; color: #5d6b7c; }
  .bar { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
  .go { border: 0; border-radius: 12px; padding: 11px 14px; font: 700 14px system-ui; cursor: pointer; }
  .shuffle { background: #3a6fd8; color: #fff; box-shadow: 0 3px 0 #2550a8; }
  .shuffle:active { transform: translateY(2px); box-shadow: none; }
  .flip { background: #fff; color: #1f2a37; box-shadow: 0 2px 0 #cfd8e3; }
  .stat { margin-left: auto; font: 600 13px system-ui; color: #5d6b7c; }
  .stat b { color: #1f2a37; }
  .room { margin-top: 14px; padding: 14px 8px 16px; border-radius: 22px; background: #fff; box-shadow: 0 10px 28px rgba(30, 60, 110, .1); display: flex; flex-direction: column; gap: 12px; }
  .room.teacher { flex-direction: column-reverse; }
  .board { padding: 8px; border-radius: 10px; background: #24443a; color: #d6ecd9; text-align: center; font: 700 12px system-ui; letter-spacing: .12em; text-transform: uppercase; }
  .grid { display: grid; grid-template-columns: 1fr 1fr 6px 1fr 1fr 6px 1fr 1fr; gap: 8px 4px; }
  .desk { position: relative; min-height: 54px; border: 0; border-radius: 10px; background: linear-gradient(#fdf6e9, #f6e8cf); box-shadow: 0 3px 0 #e2cfaa; color: #3d3220; font: 700 clamp(11.5px, 3.2vw, 14px)/1.15 system-ui; padding: 6px 1px; cursor: pointer; text-align: center; overflow-wrap: anywhere; }
  .desk.empty { background: #f3f5f8; box-shadow: none; border: 1.5px dashed #d3dae3; color: #9aa6b4; font-weight: 500; }
  .desk.pin { background: linear-gradient(#e6efff, #d3e2ff); box-shadow: 0 3px 0 #a9c1f3; color: #1e3f86; }
  .desk.pin::after { content: ""; position: absolute; top: 4px; right: 4px; width: 9px; height: 9px; border-radius: 50%; background: #3a6fd8; box-shadow: 0 0 0 2px #fff; }
  .gap { visibility: hidden; }
  .hint { margin: 10px 2px 0; font-size: 13px; color: #5d6b7c; }
  .list { margin-top: 16px; padding: 12px 14px; border-radius: 14px; background: rgba(255, 255, 255, .75); font-size: 13px; color: #4b5869; }
  .list h2 { margin: 0 0 6px; font-size: 12px; letter-spacing: .1em; text-transform: uppercase; color: #3a6fd8; }
  .list ul { margin: 0; padding: 0; list-style: none; display: flex; flex-wrap: wrap; gap: 4px 12px; }
</style>
</head>
<body>
<main class="page">
  <div class="k">Seating chart</div>
  <h1>Room 12 &middot; Period 3</h1>
  <p class="sub">Names come from the class list below. Tap a desk to keep that student in place, then shuffle the rest.</p>
  <div class="bar">
    <button class="go shuffle" id="shuffle">Shuffle seats</button>
    <button class="go flip" id="flip">Teacher's view</button>
    <span class="stat" id="stat"></span>
  </div>

  <section class="room" id="room">
    <div class="board" id="board">Front of the room</div>
    <div class="grid" id="grid"></div>
  </section>
  <p class="hint">Blue desks stay put when you shuffle. Teacher's view turns the chart around so the front is at the bottom, the way you see the room from your desk.</p>

  <section class="list">
    <h2>Class list</h2>
    <ul id="names">
      <li>Aiden</li><li>Bella</li><li>Carlos</li><li>Dina</li><li>Eli</li><li>Farah</li>
      <li>Gabe</li><li>Hannah</li><li>Isaac</li><li>Jada</li><li>Kenji</li><li>Lena</li>
      <li>Marcus</li><li>Nadia</li><li>Omar</li><li>Priya</li><li>Quinn</li><li>Rosie</li>
      <li>Sami</li><li>Tara</li><li>Umar</li><li>Violet</li>
    </ul>
  </section>
</main>
<script>
  const DESKS = 24;
  const grid = document.getElementById('grid');
  let seats = [];           // index = desk, value = name or ''
  const pinned = new Set(); // desk indexes that stay put

  function names() { return [...new Set([...document.querySelectorAll('#names li')].map((li) => li.textContent.trim()).filter(Boolean))]; }
  function shuffle(a) { for (let i = a.length - 1; i > 0; i--) { const j = Math.floor(Math.random() * (i + 1)); [a[i], a[j]] = [a[j], a[i]]; } return a; }

  function seat(animate) {
    // Read the class list fresh each time, so an edited list is used straight away.
    const list = names().slice(0, DESKS);
    const keep = new Map();
    pinned.forEach((d) => { if (seats[d] && list.includes(seats[d])) keep.set(d, seats[d]); else pinned.delete(d); });
    const rest = shuffle(list.filter((n) => ![...keep.values()].includes(n)));
    const free = shuffle([...Array(DESKS).keys()].filter((d) => !keep.has(d)));
    const before = rects();
    seats = Array(DESKS).fill('');
    keep.forEach((n, d) => (seats[d] = n));
    rest.forEach((n, i) => (seats[free[i]] = n));
    render();
    if (animate) fly(before);
  }

  function rects() {
    const r = {};
    grid.querySelectorAll('.desk').forEach((el) => { if (el.dataset.name) r[el.dataset.name] = el.getBoundingClientRect(); });
    return r;
  }

  function fly(before) {
    grid.querySelectorAll('.desk').forEach((el) => {
      const b = before[el.dataset.name]; if (!b) return;
      const a = el.getBoundingClientRect();
      const dx = b.left - a.left, dy = b.top - a.top;
      if (!dx && !dy) return;
      el.animate([{ transform: 'translate(' + dx + 'px,' + dy + 'px) scale(1.06)', zIndex: 2 }, { transform: 'none' }], { duration: 650 + Math.random() * 250, easing: 'cubic-bezier(.2,.8,.2,1)' });
    });
  }

  function render() {
    grid.innerHTML = '';
    const teacher = document.getElementById('room').classList.contains('teacher');
    for (let p = 0; p < DESKS; p++) {
      const d = teacher ? DESKS - 1 - p : p; // teacher's view is the same room turned around
      if (p % 6 === 2 || p % 6 === 4) { const g = document.createElement('span'); g.className = 'gap'; grid.append(g); }
      const b = document.createElement('button');
      const n = seats[d];
      b.className = 'desk' + (n ? '' : ' empty') + (pinned.has(d) ? ' pin' : '');
      b.textContent = n || 'empty';
      if (n) b.dataset.name = n;
      b.onclick = () => { if (!seats[d]) return; pinned.has(d) ? pinned.delete(d) : pinned.add(d); render(); };
      grid.append(b);
    }
    const count = seats.filter(Boolean).length;
    document.getElementById('stat').innerHTML = '<b>' + count + '</b> students &middot; <b>' + (DESKS - count) + '</b> empty &middot; <b>' + pinned.size + '</b> kept';
  }

  document.getElementById('shuffle').onclick = () => seat(true);
  document.getElementById('flip').onclick = (e) => {
    const before = rects();
    const t = document.getElementById('room').classList.toggle('teacher');
    e.target.textContent = t ? "Students' view" : "Teacher's view";
    document.getElementById('board').textContent = t ? 'Front of the room (you are here)' : 'Front of the room';
    render(); fly(before);
  };
  seat(false);
</script>
</body>
</html>
A classroom seating chart: names come from the class list below the room. Tap desks to keep students in place, shuffle the rest, and switch to the teacher's view from the front. All names are made up. Edit the code and the example reruns.

Getting it to your guests

You have a seating chart maker of your own. The AI gave it to you as HTML code, though, and a guest at the door cannot do anything with code.

The first wall is getting the page onto a hundred phones. Attach the file to a message and on a phone it opens as a download or as a page of code. To be something a guest taps, the page has to live somewhere.

In NOS you paste the code into a document and it becomes the seating chart straight away. Choose Create share link. Put the link in the message you already send the week before, or on a small sign at the door with a QR code. Wedding invitation QR code covers the QR side.

AI reply, NOS document, share link, and where the guests see it: a sign at the door or the message you already send.
AI reply, NOS document, share link, and where the guests see it: a sign at the door or the message you already send.

The second wall is the last-minute change. Rosa's plus-one cancels and she moves to table 2. Someone spells a name "Jonh". This happens the night before, and it happens more than once.

If you ask the AI again, you wait for a new reply. It often redraws the floor plan or changes the search while it is at it, so you check every table again. Then you have a new file and a new link, and half the guests already have the old one.

In NOS you click the guest's line in the list on the page and change the table number or fix the name. The link stays the same. Rosa searches again and sees table 2, and the floor plan shows her there.

Rosa's table number changed from 6 to 2 in the guest list on the page in NOS. On her phone, the same link now says Table 2.
Rosa's table number changed from 6 to 2 in the guest list on the page in NOS. On her phone, the same link now says Table 2.

Before you share it

  • Search for five names, including someone with an accent and someone with two last names.
  • Check every table count against your chairs.
  • Leave out anything private. The page shows every guest's name and table to anyone with the link.
  • Keep a printed list at the door for guests without a phone.

For the rest of the day's details in one place, see Share wedding details with guests. For the running order, see Wedding timeline template.

Questions people ask

How do guests find their table at a wedding?

Usually from a board or escort cards at the door. A seating chart page adds a search box: each guest types their name on their own phone and sees their table and who they sit with.

Can AI make a seating chart for me?

Yes. Give it your guest list with a table number for each person and ask for one complete HTML file. Ask it to read the names from a visible list on the page and to use only your names.

What if a table has too many people?

Ask the page to count each table. The wedding example shows 8 / 8 under each table number and turns the count red when a table is over.

Can I use it for a classroom?

Yes. The second example seats a class from a name list, keeps chosen students in place, and shuffles the rest. It can also turn the room around to your view from the front.

Can I move a guest after I have sent the link?

Yes. In NOS you click that guest's table number in the list on the page and type the new one. Everyone who opens the same link sees the new table.

Keep reading