Beacon Chat

Embeddable website chat assistant — instant answers, lead capture and optional AI.

Overview

Beacon Chat is a polished chat assistant for any website: a floating launcher, a chat panel with typing animation and quick-reply chips, instant answers from your own questions-and-answers list, lead capture (name + e-mail), business hours, "talk to a person" contact options and optional AI answers through your own server endpoint.

It is one JavaScript file (about 20 KB gzipped, no dependencies, no build step). Styles live inside a Shadow DOM, so your site's CSS cannot break the widget and the widget cannot break your site. Everything works without any server or key — the AI and the e-mail notifications are optional extras.

  • Works on any site: plain HTML, WordPress, Shopify, Webflow, Wix (custom code), React/Vue/Next apps.
  • Phones get a full-screen chat; desktops get a floating panel.
  • English and English text built in, every text replaceable, full right-to-left support.

Quick start

1. Upload dist/beacon-chat.js to your site (for example to /js/beacon-chat.js).

2. Paste this before </body> and change the texts:

<script src="/js/beacon-chat.js"></script>
<script>
  BeaconChat.init({
    brand: { name: 'Brightside Coffee' },
    accent: '#b4532a',
    greeting: ['Hi! How can I help you today?'],
    chips: ['Opening hours', 'Menu', { label: 'Talk to a person', action: 'human' }],
    knowledge: [
      { title: 'Opening hours', q: ['open', 'hours', 'close', 'what time'],
        a: 'We are open every day from 7am to 7pm.' },
      { title: 'Menu', q: ['menu', 'food', 'drinks', 'coffee'],
        a: 'Seasonal espresso, filter, pastries and lunch.',
        links: [{ label: 'See the menu', href: '/menu.html' }] }
    ],
    contact: [{ type: 'email', label: 'Email us', detail: 'hello@example.com', href: 'mailto:hello@example.com' }]
  });
</script>

3. Open your page — the launcher appears in the bottom corner. That's it.

No-code alternatives: set window.BeaconChatConfig = {…} before the script tag, or keep the configuration in a JSON file: <script src="beacon-chat.js" data-config="/chat-config.json"></script>.

Try the examples/playground.html page: change colours, theme, position and language, and copy the generated code.

Features

  • Instant answers from your knowledge list — keyword, phrase and typo-tolerant matching, plural/suffix handling for English and Persian, related-topic suggestions.
  • Quick-reply chips globally and per answer; chips can send a question, open a link, open the lead form or the contact options.
  • Rich answers: **bold**, *italic*, [links](url), line breaks, link buttons, optional image, multi-bubble answers.
  • Lead capture: name, e-mail, phone, message with validation, honeypot and consent text; e-mails typed in the chat are detected automatically. Send to your server, open a prefilled e-mail, or handle in JavaScript.
  • Business hours in any time zone: header shows "Online" or "Away · back tomorrow at 07:00"; contact channels can hide outside hours.
  • Talk to a person: phone, e-mail, messenger or any link as contact cards, plus "leave a message".
  • Optional AI answers through server/chat.php with your own provider key — as a fallback for unknown questions or for every question.
  • Teaser bubble and unread badge to start conversations, optional auto-open.
  • Themes: light, dark or automatic; any accent colour or gradient; text colour on the accent picked automatically for contrast; radius, size, font, position, offsets.
  • RTL and i18n: built-in English and Persian, override any text, localised digits and day names.
  • Mobile full-screen with safe-area support; body scroll locked while open.
  • Conversation memory per tab (session) or across visits (local) or off.
  • Accessible: real buttons and labels, role="log" live region, keyboard (Enter to send, Shift+Enter new line, Esc to close), visible focus, reduced-motion and forced-colours support.
  • JavaScript API and events for analytics, CRMs and custom flows.

File structure

beacon-chat/
├── dist/
│   └── beacon-chat.js          ← the widget (the only file your site needs)
├── examples/
│   ├── index.html              ← Brightside Coffee demo (light, English)
│   ├── persian.html            ← bookstore demo (dark, right-to-left, Persian)
│   ├── playground.html         ← try options live + copy the code
│   └── assets/                 ← demo configs, demo styles, photos, fonts
├── server/                     ← optional PHP endpoint
│   ├── chat.php                ← AI answers + lead saving/e-mail
│   ├── config.sample.php       ← copy to config.php and fill in
│   ├── knowledge.sample.json   ← knowledge file example (widget + server)
│   ├── test.html               ← checks your server setup
│   └── .htaccess               ← blocks config and data files on Apache
├── documentation/index.html    ← this page
├── README.md · CHANGELOG.md · LICENSE.txt · THIRD-PARTY.txt

The examples/assets/configs.js file holds the two complete demo configurations — the fastest way to start is to copy one of them.

Configuration

Every option is optional. Pass them to BeaconChat.init({…}).

OptionDefaultWhat it does
id'default'Unique name. Needed only for several widgets on one page; also the storage key.
brand{ name: 'Assistant' }name, subtitle (replaces the status line while online), avatar (image URL) or initials.
accent / accent2'#5b5bf0' / sameMain colour and optional gradient end colour (launcher, header, your bubbles).
theme'light''light', 'dark' or 'auto' (follows the visitor's system).
position'right''right' or 'left' bottom corner. offset: { x: 24, y: 24 } moves it.
width / height / radius380 / 620 / 20Panel size in px on desktop and corner radius.
fontsystem fontsCSS font-family list, e.g. "Inter, sans-serif" (load the font on your page).
zIndex2147483000Stacking order if something on your page covers the widget.
lang / dir'en' / 'auto'Language of built-in texts ('en', 'fa'); dir is 'auto', 'ltr' or 'rtl'.
texts{}Override any built-in text — see "Languages & RTL".
launcher{ label: '', icon: 'chat' }Launcher label (empty = round button) and icon: 'chat', 'help', 'spark'.
greeting['Hi! How can I help…']First message(s), shown one after another with typing animation.
chips[]Quick replies under the greeting and after answers without own chips.
knowledge / knowledgeUrl[] / ''Your questions and answers, inline or loaded from a JSON file.
threshold2Minimum match score for an answer. Higher = stricter.
fallbackbuilt-in textAnswer when nothing matches: a string or { text, links, chips }.
contact[]Contact cards for “talk to a person”: { type, label, detail, href, always }.
lead{ enabled: true, … }Lead form — see "Lead capture".
hoursnullBusiness hours — see "Business hours".
ai{ enabled: false }{ enabled, endpoint, mode: 'fallback' | 'always', history: 12 }.
teasernull{ title, text, delay: 4000, once: true } — bubble next to the launcher.
autoOpenfalsetrue or a delay in ms to open the chat automatically.
typingSpeed1Typing-indicator speed multiplier; 0 answers instantly.
persist'session''session' (this tab), 'local' (remember between visits) or false.
poweredBynullSmall footer line: { text, href }.
onReady / onOpen / onClose / onMessage / onLeadnullCallbacks — see "JavaScript API".

Knowledge base

Each entry has keywords (q) and an answer (a). The widget scores every entry against the visitor's message and replies with the best match above threshold.

{
  id: 'events',                       // optional, shows up in events/analytics
  title: 'Book the space for an event',// used for "Related" suggestions and the tester
  q: ['event', 'private hire', 'party', 'birthday', 'book'],
  a: 'Our back room seats 24. Packages start at **£180**.',
  // a: ['First bubble', 'Second bubble'] → several messages
  links: [{ label: 'See packages', href: '/events.html' }],
  chips: [{ label: 'Ask about a date', action: 'lead' }, 'Catering'],
  image: '/img/room.jpg', imageAlt: 'The back room',
  action: 'lead',                     // optional: 'lead' opens the form, 'human' the contact options
  weight: 1.2                         // optional: boost or lower this entry
}

How matching works

  • A multi-word keyword found as a phrase scores highest ('private hire').
  • Single words match whole words, plurals and simple endings (book ↔ booking, bookings), word starts (reserv ↔ reservation) and one-letter typos for words of 5+ letters.
  • The included matching dictionary and sample content are in English. Customise your own messages in the configuration.
  • Greetings, thanks and goodbyes get friendly built-in replies; “human / agent / operator …” opens the contact options. Keywords for these live in texts.intents.

Use the knowledge tester on examples/playground.html or call chat.match('your question') in the browser console to see scores.

Placeholders in answers

{today} today's opening hours · {brand} brand name · {name} the visitor's name after the lead form.

Loading from a file

knowledgeUrl: '/chat-knowledge.json' loads an array of entries or an object { knowledge, chips, greeting, contact } (see server/knowledge.sample.json). The server endpoint can use the same file as facts for AI answers.

Lead capture

lead: {
  enabled: true,
  fields: ['name', 'email', 'phone', 'message'],  // any order, any subset
  required: ['name', 'email'],                    // default: name + email
  title: 'Leave your details', intro: 'We reply within a few hours.',
  consent: 'We only use your details to reply. [Privacy](/privacy.html)',
  thanks: 'Thanks {name}! We will e-mail {email} soon.',
  endpoint: '/beacon/server/chat.php',            // POST JSON to your server
  mailto: '',                                     // or open a prefilled e-mail to this address
  askOnFallback: true                             // offer "Leave a message" when no answer matches
}

The form opens from a chip or entry with action: 'lead', from “Leave a message” in the contact card, from chat.startLead(), and automatically when a visitor types an e-mail address. The visitor's last question is prefilled into the message, and the last 20 messages of the conversation are sent along as a transcript.

Where it goes: endpoint (recommended, the included server/chat.php saves a CSV and e-mails you), mailto (no server at all), or neither — then handle it in onLead(lead) (e.g. send to your CRM or form service).

Business hours

hours: {
  timezone: 'Europe/London',            // any IANA time zone
  days: {
    mon: '09:00-17:30', tue: '09:00-17:30', wed: '09:00-17:30',
    thu: '09:00-17:30', fri: ['09:00-12:00', '13:00-17:00'],
    sat: 'closed', sun: 'closed'
  }
}

Inside the hours the header says “Online · replies instantly”; outside it says “Away · back tomorrow at 09:00” and the dot turns amber. Outside hours only e-mail contacts (and those with always: true) are shown in “talk to a person”, together with “Leave a message”.

Talk to a person

contact: [
  { type: 'phone', label: 'Call us', detail: '+44 20 0000 0000', href: 'tel:+442000000000' },
  { type: 'whatsapp', label: 'Message us', detail: 'Replies in minutes', href: 'https://example.com/your-chat-link' },
  { type: 'email', label: 'Email', detail: 'hello@example.com', href: 'mailto:hello@example.com' },
  { type: 'link', label: 'Book a call', href: '/book.html', always: true }
]

Types set the icon: phone, email, whatsapp/messenger/chat/message, link. The cards appear when the visitor asks for a person, presses the person icon in the header or a chip with action: 'human'.

AI answers & server endpoint

The widget never talks to an AI provider directly (that would expose your key). Instead server/chat.php — a single PHP file, no database — forwards questions to the provider of your choice with your own key, and saves leads.

Setup (5 minutes)

  • Upload the server/ folder, e.g. to /beacon/server/. PHP 7.4+ with cURL (or allow_url_fopen).
  • Copy config.sample.php to config.php. Fill in api_url, model and your key (or set the BEACON_AI_KEY environment variable), set ai.enabled to true and your notify_email.
  • Open /beacon/server/test.html and press the two buttons to check everything.
  • Point the widget at it:
ai:   { enabled: true, endpoint: '/beacon/server/chat.php', mode: 'fallback' },
lead: { enabled: true, endpoint: '/beacon/server/chat.php' }

Providers

Any provider with a “chat completions” style API works with the default format: 'chat' (system message + messages, answer in choices[0].message.content), including self-hosted model servers. For APIs that take a top-level system field use format: 'system-field'. Custom auth headers (headers), the answer location (reply_path, dotted path) and extra body fields (extra) are configurable, so other APIs can be connected without editing PHP.

Modes

mode: 'fallback' (default) answers from your knowledge list first and asks the AI only when nothing matches — fast and cheap. mode: 'always' sends every question to the AI. If the server is down, rate-limited or not configured, the widget silently falls back to the knowledge answers.

What the AI knows

Your instructions (or a friendly default using business_name) plus every entry of knowledge_file as a facts list. It is told to answer briefly in the visitor's language and never invent prices or promises.

Security

  • Key stays on the server; .htaccess blocks config.php, JSON/CSV files and data/ on Apache. On Nginx add location ~ /server/(data/|config|.*\.(json|csv)) { deny all; } or move data_dir outside the web root.
  • Per-IP rate limits for AI answers and leads, request-size limit, same-origin by default (allowed_origins for other domains), honeypot, e-mail header injection and spreadsheet formula injection protection.

Languages & RTL

Set lang: 'fa' for the built-in English text (right-to-left layout is switched on automatically for fa, ar, he, ur …). For any other language set lang and translate the texts you need:

BeaconChat.init({
  lang: 'de',
  texts: {
    placeholder: 'Nachricht schreiben…', online: 'Online · antwortet sofort',
    leadTitle: 'Ihre Kontaktdaten', leadName: 'Name', leadEmail: 'E-Mail',
    leadThanks: 'Danke, {name}! Wir melden uns bei {email}.',
    fallback: 'Dazu habe ich leider keine Antwort…',
    days: ['Sonntag', 'Montag', 'Dienstag', 'Mittwoch', 'Donnerstag', 'Freitag', 'Samstag'],
    intents: { greet: ['hallo', 'guten tag'], thanks: ['danke'], human: ['mitarbeiter', 'mensch'] }
  }
});

All text keys are listed at the top of dist/beacon-chat.js (LOCALES.en). Times and digits are formatted for the language automatically.

JavaScript API

const chat = BeaconChat.init({...});   // or BeaconChat.get('id')
chat.open(); chat.close(); chat.toggle(); chat.isOpen();
chat.send('What are your opening hours?');  // as if the visitor typed it
chat.say('Psst — 10% off beans today!');    // bot message
chat.showChips(['Menu', 'Hours']);
chat.startLead({ email: 'known@example.com' });
chat.talkToHuman();
chat.reset();                               // clear the conversation
chat.match('do you have vegan food');       // → [{ id, title, score }]
chat.destroy();

chat.on('message', m => {});  // { role: 'user' | 'bot', text, links, ai, ts }
chat.on('lead', lead => {});  // { name, email, phone, message, transcript, page, … }
chat.on('open', () => {}); chat.on('close', () => {});
chat.on('link', l => {});     // a link in the chat was clicked
chat.on('contact', c => {});  // a contact card was used

Open the chat from your own buttons: <button onclick="BeaconChat.get().open()">Chat with us</button>.

Customise & rebrand

  • Colours: accent and accent2; dark/light via theme. The text colour on the accent is chosen automatically for contrast.
  • Look: radius, width, height, font, launcher label and icon, brand.avatar for your logo or a team photo.
  • Deeper styling: the CSS is the CSS array near the top of dist/beacon-chat.js, using custom properties (--bc-bg, --bc-ink, --bc-bot, …). Edit it directly — there is no build step.
  • White label: nothing in the widget shows our name. poweredBy is off by default.
  • Several widgets: give each an id (e.g. one per language).

FAQ

Do I need a server or an API key?

No. Knowledge answers, chips, hours, contact cards and the lead form (with mailto or onLead) all work on a static site. The server is only for AI answers and saving/e-mailing leads.

Will it slow my site down?

No. One small file, no requests until the visitor interacts (except knowledgeUrl, if you use it), no fonts or images loaded by default.

Does it work with WordPress / Shopify / Webflow?

Yes — upload the JS file (or paste it into a custom-code/footer area) and add the init snippet.

Is it GDPR-friendly?

The widget sets no cookies and loads nothing from third parties. The conversation is kept in the visitor's browser storage only if persist is on. Use lead.consent to link your privacy policy.

Can I use it on client websites?

The Licence covers one end product (your site or one client's site). The Full source licence covers one production domain for you and your clients, including white-label and SaaS use. Neither allows reselling the source code itself.

Which browsers?

All current versions of Chrome, Edge, Firefox and Safari on desktop, iOS and Android.

Support

Questions or found a bug? Write to hello@example.com or through vortaxis.com/store with your order number. Updates are listed in CHANGELOG.md.

Made by Vortaxis Studio.

Licence and production activation

Single-domain and Full-source purchases each activate one production hostname. Full source adds editable code, white-label and SaaS rights; it does not include unlimited installations or redistribution. Localhost and .test development hosts are allowed.

Set the delivered key and production hostname in LICENSING/licence.json. Use https://vortaxis.com/licence/ to view activation or request a domain transfer. Vortaxis Studio approves replacement domains. Production activation requires a network connection; public demos remain unrestricted.

See LICENSE.txt for the complete terms. Existing purchases retain their original terms.