# B-Bot

B-Bot is an animated icon mark with 24 expressions and 9 quick gestures: smile, laugh, joy, sad, confused and more. At rest it is exactly the original logo. There are no runtime dependencies.

Copyright (c) 2026 BrainLab. All rights reserved. See [LICENSE](LICENSE) for the terms of use.

**Get it:** download [b-bot.zip](https://bot.ion.bd/b-bot.zip) from https://bot.ion.bd, then unzip it. Open `index.html` to see everything running.

## Pick how to use it

| You have | Use | Files you need |
| --- | --- | --- |
| Any website, Vue, Svelte, Angular | The `<b-bot>` tag | `b-bot.global.js` or `b-bot.js` |
| React or Next.js | The `BBot` component | `b-bot.js`, `b-bot.d.ts`, `react/BBot.tsx` |
| Only a place to put an image | An animated SVG in `<img>` | One file from `assets/animated/` |

The tag and the component can change expression at any time and blend smoothly across. An image file plays one expression.

### Plain HTML

```html
<script src="b-bot.global.js"></script>

<b-bot id="bot" expression="smile" size="96" label="B-Bot"></b-bot>

<script>
  const bot = document.getElementById('bot');

  bot.expression = 'thinking';               // hold an expression
  bot.play('laugh', { duration: 2000 });     // for a while, then back to what it was holding
  bot.play('nod');                           // a quick gesture, layered over anything
</script>
```

With a bundler, use `import './b-bot.js'` instead of the script tag. A module loads after inline scripts run, so anything that calls `play()` from an inline script should wait for it: `customElements.whenDefined('b-bot').then(() => bot.play('nod'))`. Vue and Angular need telling that `b-bot` is a custom element (`isCustomElement` in Vue, `CUSTOM_ELEMENTS_SCHEMA` in Angular).

### React and Next.js

Copy `b-bot.js`, `b-bot.d.ts` and the `react` folder into your project, keeping `react/` beside the other two.

```tsx
import { useRef } from 'react';
import { BBot, type BBotHandle } from './b-bot/react/BBot';

export function Assistant({ loading }: { loading: boolean }) {
  const bot = useRef<BBotHandle>(null);

  return (
    <button onClick={() => bot.current?.play('hop')}>
      <BBot ref={bot} expression={loading ? 'thinking' : 'smile'} size={96} label="B-Bot" />
    </button>
  );
}
```

It works with server rendering: the server sends the plain logo and the browser takes over. In the Next.js App Router, put `'use client'` at the top of any file that uses a ref or click handler, like this one.

### Image file

```html
<picture>
  <source media="(prefers-reduced-motion: reduce)" srcset="assets/static/brand/laugh.svg">
  <img src="assets/animated/brand/laugh.svg" width="96" height="96" alt="B-Bot laughing">
</picture>
```

The bot arrives into the expression, then loops. Keep the `<picture>` wrapper: an image file cannot detect reduced motion by itself.

## Options

| Tag attribute | React prop | What it does |
| --- | --- | --- |
| `expression` | `expression` | The expression to hold. Default `neutral`. |
| `size` | `size` | Width and height: a number of pixels or any CSS length. Default 64. CSS `width` and `height` also work. |
| `idle` | `idle` | Random blinks and glances while neutral, like the current logo. |
| `minimal` | `minimal` | Hides tears, sparkles, question marks and so on. Use below about 40px. |
| `mono` | `mono` | Uses the surrounding text colour, for example white on a coloured button. |
| `label` | `label` | What a screen reader says. Leave it out if the bot is only decoration. |

**Colour:** the bot uses `--b-bot-color`, which defaults to `#5b73e8`.

```css
b-bot { --b-bot-color: #0f766e; }
```

**`play(name, { duration })`** plays an emotion for `duration` ms (default 1800), then returns to the held expression. For a gesture it layers the movement over whatever is showing, and `duration` is ignored. It returns a promise that resolves when it ends or something replaces it.

## Expressions

| Name | When to use it |
| --- | --- |
| `neutral` | The logo at rest. Add `idle` for occasional blinks and glances. |
| `idle` | A calm blink-and-glance loop for when nothing is happening. |
| `smile` | Greeting, friendly confirmation, default positive reply. |
| `happy` | Task finished, something went well. |
| `laugh` | Something funny. Fast and bouncy, so best for short moments. |
| `joy` | Big win: success, milestone, celebration. |
| `love` | Thanks, favourites, an appreciative moment. |
| `excited` | New feature, a great idea, can't-wait energy. |
| `wink` | Playful, a tip or a shared secret. |
| `smug` | Confident: "told you so", "leave it to me". |
| `shy` | A compliment or a thank-you received. |
| `sad` | Sorry, could not help, no results. |
| `cry` | Serious failure, data lost, a heartfelt sorry. |
| `angry` | Blocked or refused; playful frustration. Use sparingly. |
| `worried` | A warning, a risky action, "are you sure?". |
| `oops` | A small mistake, retrying, "my bad". |
| `confused` | Did not understand: ask the person to rephrase. |
| `surprised` | Unexpected input or an unexpected result. |
| `curious` | Asking a follow-up question, showing interest. |
| `thinking` | Waiting for the model: use while a reply loads. |
| `listening` | Voice input is live, or the person is typing. |
| `talking` | While a reply streams or voice output plays. |
| `sleepy` | Offline, inactive, away, or quiet hours. |
| `dizzy` | Too many requests, overload, a playful error. |

**Gestures for `play()`:** `blink`, `quickWink`, `lookLeft`, `lookRight`, `nod`, `shake`, `jolly`, `tilt`, `hop`.

The same list is available in code as `EXPRESSION_INFO` and in `assets/expressions.json`.

## A typical chat flow

```js
bot.expression = 'listening';   // the person is typing
bot.expression = 'thinking';    // request sent
bot.expression = 'talking';     // reply streaming
bot.play('happy', { duration: 1500 });   // reply finished
bot.expression = 'smile';       // and rest
```

Switch as often as you like: the bot blends from wherever it currently is, even mid-movement.

## Good to know

- **Reduced motion:** the tag and component show a still pose and stop moving for people who ask for reduced motion. No code needed.
- **Performance:** one shared animation loop drives every bot. A bot off screen or at rest does no work.
- **Space around the bot:** jumps and tilts go slightly outside its box. The tag and component allow this, so a parent with `overflow: hidden` needs some padding. Image files already include the space, so the bot looks about 20% smaller in a same-sized `<img>`; make the image about 1.27 times bigger to match.
- **Browsers:** current Chrome, Edge, Firefox and Safari. All the moving parts are standard SVG. Tested so far in Chromium only.

## What's in the download

```
b-bot.global.js        script-tag version
b-bot.js, .min.js      ES module versions
b-bot.d.ts             TypeScript types
react/BBot.tsx         React component
assets/animated/          self-running SVGs, in brand/ and white/
assets/static/            one still pose per name, in brand/ and white/
assets/source/            the original logo, untouched
assets/expressions.json   every name, label and when to use it
project/                  the full editable source
LICENSE                   copyright and terms of use
```

## Changing or adding expressions

To change the bot itself, work in the `project` folder of the download. It needs Node 22.2 or newer.

Every expression is one entry in `src/expressions.js`. Add or edit one, add a new name to `types/b-bot.d.ts`, then:

```bash
npm install
npm run build
npm run check
```

`npm run build` regenerates the whole of `dist/`, including every SVG, and fails if a movement would be cut off at the edge of an image file. `npm run serve` serves the demo at http://localhost:4173, and `tools/filmstrip.html` shows each expression frame by frame.
