# 🎬 Liveposter

A JSON-driven JavaScript animation library for creating sophisticated image transitions and effects. Framework-agnostic, lightweight, and easy to use.

## Features

- **JSON-driven**: Define animations declaratively via JSON specifications
- **Framework-agnostic**: Works with vanilla JS, React, Vue, or any other framework
- **Multiple animation modes**: Slideshow, fade, pan, lenticular, parallax, zoom
- **Augmented Reality (AR)**: WebXR-powered AR experiences with automatic target generation
- **Input-driven animations**: Respond to mouse, tilt, scroll inputs
- **Extensible**: Easy to add custom modes and effects
- **Zero dependencies**: Core library has no external dependencies
- **ES6 modules**: Modern JavaScript with tree-shaking support

## Quick Start

### Try the Demo

**Default demo (all animation modes):**

```bash
npx liveposter
# or
npx liveposter demo
```

**Poster viewer (fullscreen vertical viewer with swipe/arrow navigation):**

```bash
npx liveposter poster-viewer/index.html
```

**Serve a single poster spec:**

```bash
npx liveposter path/to/your-poster.json
```

**Serve a poster list (multiple posters in fullscreen layout):**

```bash
npx liveposter path/to/poster-list.json
```

**Serve custom HTML file:**

```bash
npx liveposter path/to/your/file.html
```

**Development mode with hot-reload:**

```bash
# Watch default demo with auto-reload
npx liveposter dev

# Watch a single poster spec with auto-reload
npx liveposter dev path/to/your-poster.json

# Watch a poster list with auto-reload
npx liveposter dev path/to/poster-list.json
```

**Launch in fullscreen kiosk mode:**

```bash
# Default demo in kiosk mode
npx liveposter kiosk

# Specific poster in kiosk mode
npx liveposter kiosk path/to/your-poster.json

# Poster list in kiosk mode
npx liveposter kiosk path/to/poster-list.json
```

**Augmented Reality (AR) commands:**

```bash
# Generate AR target files (.mind) from your images
# Automatically processes all JPG, PNG, GIF images in your specs
npx liveposter ar-compile-targets poster.json

# Development server for AR experiences (port 3100)
npx liveposter ar-dev poster.json

# Build static AR site for deployment
npx liveposter ar-build poster.json --output dist-ar
```

This will start a demo server at http://localhost:3000 (AR server runs on port 3100).

### Port Configuration

The demo server uses port 3000 by default. You can customize the port in three ways (in priority order):

**Option 1: Environment variable (highest priority)**
```bash
PORT=8080 npx liveposter dev
PORT=8080 npx liveposter start
```

**Option 2: .env file in current directory**
```bash
# Create .env in your project directory
echo "PORT=8080" > .env
npx liveposter dev my-poster.json
```

**Option 3: .env file in package directory**
```bash
# Create .env in liveposter package directory
echo "PORT=8080" > packages/demo-server/.env
npm run dev
```

The server checks these locations in order and uses the first PORT value it finds. If none are set, it defaults to port 3000.

### Development Workflow

When developing a poster, use the development mode with automatic hot-reload:

**Using npx (recommended):**

```bash
# Watch any poster spec with automatic browser reload
npx liveposter dev path/to/your-poster.json

# Watch a poster list
npx liveposter dev path/to/poster-list.json

# Watch default demo
npx liveposter dev
```

The dev mode uses `nodemon` to automatically restart the server and reload the browser when you modify:

- Poster spec JSON files
- Image assets
- HTML/CSS files
- Any files in the spec's directory

**Example workflow:**

```bash
# 1. Create your poster spec anywhere
mkdir ~/my-posters
cd ~/my-posters
cat > my-poster.json << EOF
{
  "container": "#poster",
  "mode": "diaporama",
  "timing": { "duration": 2000, "transition": 500 },
  "effects": { "type": "fade", "easing": "ease-in-out" },
  "images": [
    { "src": "https://picsum.photos/1080/1920?random=1", "alt": "Image 1" },
    { "src": "https://picsum.photos/1080/1920?random=2", "alt": "Image 2" }
  ]
}
EOF

# 2. Run dev mode with hot-reload
npx liveposter dev my-poster.json

# 3. Open http://localhost:3000
# 4. Edit my-poster.json - server auto-restarts and browser automatically refreshes!
```

**From repository (for library development):**

```bash
# Watch a specific poster spec
cd packages/demo-server
npm run dev -- path/to/your-poster.json

# Watch a poster list
npm run dev -- path/to/poster-list.json
```

### Poster List Format

Create a `poster-list.json` file to display multiple posters in a fullscreen layout:

```json
[
  "specs/poster1.json",
  "specs/poster2.json",
  {
    "spec": "specs/poster3.json",
    "title": "Custom Title",
    "description": "Optional description"
  }
]
```

Paths are resolved relative to the poster list location. The server will:

- Load each spec file
- Resolve image paths relative to each spec's location
- Generate a fullscreen viewer with keyboard navigation (↑/↓ arrows)

### Installation

```bash
npm install @liveposter/core
```

### Basic Usage

```html
<!DOCTYPE html>
<html>
  <head>
    <link
      rel="stylesheet"
      href="node_modules/@liveposter/core/styles/animator.css"
    />
  </head>
  <body>
    <div id="slideshow"></div>

    <script type="module">
      import ImageAnimator from "@liveposter/core";

      const config = {
        container: "#slideshow",
        mode: "diaporama",
        loop: true,
        timing: {
          duration: 2000,
          transition: 500,
        },
        effects: {
          type: "fade",
          easing: "ease-in-out",
        },
        images: [
          { src: "image1.jpg", alt: "Image 1" },
          { src: "image2.jpg", alt: "Image 2" },
          { src: "image3.jpg", alt: "Image 3" },
        ],
      };

      const animator = new ImageAnimator(config);
      await animator.init();
    </script>
  </body>
</html>
```

## Available Animation Modes

- **hardcut-slideshow**: Instant cuts between images
- **diaporama**: Smooth crossfade transitions
- **pan-slideshow**: Continuous panning across wide images
- **lenticular**: Input-driven image switching (simulates lenticular printing)
- **parallax-layers**: Multiple layers with depth effect
- **zoom-slideshow**: Ken Burns style zoom effect

## Project Structure

This is a monorepo managed with **Turborepo** containing:

- **@liveposter/core**: Main animation library (publishable)
- **@liveposter/demo-server**: Demo server with CLI (`npx liveposter`)
- **@liveposter/demo-projects**: Demo projects (poster-viewer, etc.)

## Development

### Install Dependencies

```bash
npm install
```

### Configuration

Configure the demo server (optional):

```bash
cp packages/demo-server/.env.example packages/demo-server/.env
# Edit .env to change PORT (default: 3000)
```

### Build the Library

```bash
npm run build
```

### Run Demo Server

```bash
# Development mode (hot reload)
npm run dev

# Production mode
npm run start
```

### Kiosk Mode Deployment

For digital signage or fullscreen display installations, use the cross-platform kiosk launcher:

**Using npx (recommended for production):**

```bash
# Default demo in kiosk mode
npx liveposter kiosk

# Serve a specific poster in kiosk mode
npx liveposter kiosk path/to/your-poster.json

# Serve a poster list in kiosk mode
npx liveposter kiosk path/to/poster-list.json
```

**From repository (for development):**

```bash
# Default demo
npm run kiosk

# With custom poster (edit launch-kiosk.js to pass arguments)
node launch-kiosk.js path/to/your-poster.json
```

This will:

1. Automatically kill any process using port 3000
2. Start the server with your poster(s)
3. Wait for it to be ready
4. Launch Chrome in fullscreen kiosk mode
5. Works on all platforms (Windows, macOS, Linux) without modification

**Manual launch by platform:**

**macOS:**

```bash
/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --kiosk --app=http://localhost:3000
```

**Windows:**

```cmd
chrome --kiosk --app=http://localhost:3000
```

**Linux:**

```bash
chromium-browser --kiosk --app=http://localhost:3000
# or
google-chrome --kiosk --app=http://localhost:3000
```

**Additional kiosk mode options:**

```bash
# Disable gestures and prevent exiting (Linux/macOS)
chromium-browser --kiosk --app=http://localhost:3000 \
  --disable-pinch \
  --overscroll-history-navigation=0 \
  --disable-features=TranslateUI
```

**Auto-start on boot (Linux/Raspberry Pi):**

Create `/etc/systemd/system/liveposter.service`:

```ini
[Unit]
Description=Liveposter Kiosk
After=network.target

[Service]
Type=simple
User=pi
WorkingDirectory=/home/pi/liveposter
Environment=DISPLAY=:0
ExecStart=/usr/bin/npm run kiosk
Restart=always

[Install]
WantedBy=graphical.target
```

Enable and start:

```bash
sudo systemctl enable liveposter
sudo systemctl start liveposter
```

### Testing the CLI Locally

To test the `npx liveposter` command before publishing, use npm link:

**1. Link the package globally:**

```bash
cd packages/demo-server
npm link
```

**2. Test the CLI commands:**

```bash
# From anywhere on your system
liveposter                    # Default demo
liveposter demo              # Explicit demo command
liveposter poster-viewer/index.html
liveposter path/to/poster.json
liveposter path/to/poster-list.json
```

**3. Unlink when done:**

```bash
npm unlink -g @liveposter/demo-server
```

**Alternative: Test scripts without npm link**

From the repository root:

```bash
# Test server directly (without CLI wrapper)
npm run test:demo              # Default demo
npm run test:poster-viewer     # Poster viewer
npm run test:single-poster     # Single poster spec
npm run test:poster-list       # Poster list

# Test CLI wrapper (simulates npx liveposter)
npm run test:cli               # Default demo
npm run test:cli:demo          # Explicit demo
npm run test:cli:poster-viewer # Poster viewer
npm run test:cli:single        # Single spec
npm run test:cli:list          # Poster list
```

From demo-server package:

```bash
cd packages/demo-server

# Test server directly
npm run test:demo
npm run test:poster-viewer
npm run test:single-poster
npm run test:poster-list
npm run test:custom-html
```

See [DEVELOPMENT.md](./DEVELOPMENT.md) for detailed development workflow.

### Workspace Structure

```
liveposter/
├── turbo.json            # Turborepo configuration
├── packages/
│   ├── core/             # @liveposter/core - Main library
│   │   ├── src/          # Source files (ES6 modules)
│   │   ├── styles/       # Base CSS styles
│   │   ├── dist/         # Build output (CJS, ESM, UMD)
│   │   └── .npmignore    # NPM publish configuration
│   │
│   ├── demo-server/      # Demo server & CLI
│   │   ├── bin/          # CLI entry point (npx liveposter)
│   │   ├── server.js     # Express server
│   │   └── public/       # Default demo files & specs
│   │
│   └── demo-projects/    # Demo projects
│       ├── poster-viewer/   # Fullscreen poster viewer
│       │   ├── index.html
│       │   ├── styles.css
│       │   └── spec.json
│       └── public/
│           └── images/      # Placeholder poster images (SVG)
```

## API

### ImageAnimator

```javascript
const animator = new ImageAnimator(config);

// Initialize
await animator.init();

// Control
animator.pause();
animator.resume();
animator.goToSlide(index);
animator.next();
animator.previous();

// Events
animator.on("slideChange", (data) => {
  console.log("Current slide:", data.currentIndex);
});

// Cleanup
animator.destroy();
```

### Loading from External JSON

```javascript
const animator = await ImageAnimator.loadFromFile("config.json", "#container");
```

## JSON Configuration Schema

See [REFERENCE.md](./REFERENCE.md) for the complete API reference.

Basic structure:

```json
{
  "mode": "diaporama",
  "loop": true,
  "timing": {
    "duration": 2000,
    "transition": 500
  },
  "effects": {
    "type": "fade",
    "easing": "ease-in-out"
  },
  "images": [{ "src": "image1.jpg" }]
}
```

## Animation Modes

| Mode                | Description           | Use Case                 |
| ------------------- | --------------------- | ------------------------ |
| `hardcut-slideshow` | Instant transitions   | Fast product showcases   |
| `diaporama`         | Crossfade transitions | Classic slideshows       |
| `pan-slideshow`     | Continuous panning    | Panoramic images         |
| `zoom-slideshow`    | Ken Burns zoom effect | Cinematic presentations  |
| `lenticular`        | Mouse/tilt-driven     | Interactive 3D effect    |
| `parallax-layers`   | Multi-layer depth     | Interactive depth scenes |

## Overlay Types

Overlays add visual elements on top of images:

- **gridline**: Grid overlay (rule of thirds)
- **vignette**: Edge darkening
- **image**: Static image overlay
- **text**: Text overlay
- **gradient**: Color gradient overlay

## Configuration Options

### Timing

```json
"timing": {
  "duration": 3000,      // Display duration (ms)
  "transition": 500      // Transition duration (ms)
}
```

Per-image timing overrides:

```json
"images": [
  {
    "src": "image1.jpg",
    "timing": {
      "duration": 5000,
      "transition": 1000
    }
  }
]
```

### Effects

Mode-specific effect parameters:

```json
// Fade
"effects": { "type": "fade", "easing": "ease-in-out" }

// Pan
"effects": {
  "type": "pan",
  "direction": "horizontal",
  "easing": "linear"
}

// Zoom
"effects": {
  "type": "zoom",
  "direction": "in",
  "scale": 1.2,
  "easing": "ease-out"
}
```

### Easing Functions

- `linear`: Constant speed
- `ease-in`: Accelerates
- `ease-out`: Decelerates
- `ease-in-out`: Smooth acceleration and deceleration

### Overlays

```json
"overlays": [
  {
    "type": "gridline",
    "rows": 3,
    "columns": 3,
    "color": "rgba(255, 255, 255, 0.3)",
    "lineWidth": 2
  },
  {
    "type": "vignette",
    "color": "rgba(0, 0, 0, 0.3)",
    "intensity": 0.5
  }
]
```

## Input Types

For interactive modes (lenticular, parallax):

- **mouseX / mouseY**: Mouse position within container
- **tiltX / tiltY**: Device orientation (mobile)
- **scroll**: Scroll position

Input mapping:

```json
"input": {
  "type": "mouseX",
  "mapping": "eased"  // or "direct"
}
```

## Browser Support

- Chrome/Edge 90+
- Firefox 88+
- Safari 14+
- Mobile: iOS 14+, Android 90+

## Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

## License

MIT

## Author

Your Name / Organization

---

Built with ❤️ using modern JavaScript
