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.phpwith 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({…}).
| Option | Default | What 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' / same | Main 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 / radius | 380 / 620 / 20 | Panel size in px on desktop and corner radius. |
font | system fonts | CSS font-family list, e.g. "Inter, sans-serif" (load the font on your page). |
zIndex | 2147483000 | Stacking 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. |
threshold | 2 | Minimum match score for an answer. Higher = stricter. |
fallback | built-in text | Answer 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". |
hours | null | Business hours — see "Business hours". |
ai | { enabled: false } | { enabled, endpoint, mode: 'fallback' | 'always', history: 12 }. |
teaser | null | { title, text, delay: 4000, once: true } — bubble next to the launcher. |
autoOpen | false | true or a delay in ms to open the chat automatically. |
typingSpeed | 1 | Typing-indicator speed multiplier; 0 answers instantly. |
persist | 'session' | 'session' (this tab), 'local' (remember between visits) or false. |
poweredBy | null | Small footer line: { text, href }. |
onReady / onOpen / onClose / onMessage / onLead | null | Callbacks — 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 (orallow_url_fopen). - Copy
config.sample.phptoconfig.php. Fill inapi_url,modeland your key (or set theBEACON_AI_KEYenvironment variable), setai.enabledtotrueand yournotify_email. - Open
/beacon/server/test.htmland 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;
.htaccessblocksconfig.php, JSON/CSV files anddata/on Apache. On Nginx addlocation ~ /server/(data/|config|.*\.(json|csv)) { deny all; }or movedata_diroutside the web root. - Per-IP rate limits for AI answers and leads, request-size limit, same-origin by default (
allowed_originsfor 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:
accentandaccent2; dark/light viatheme. The text colour on the accent is chosen automatically for contrast. - Look:
radius,width,height,font,launcherlabel and icon,brand.avatarfor your logo or a team photo. - Deeper styling: the CSS is the
CSSarray near the top ofdist/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.
poweredByis 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.