# How to Use Focus, Reach, and Deep-Linking Features in the Archify Viewer

> Master Archify viewer navigation with focus, reach, and deep-linking features. Programmatically control camera, target views, and create shareable URLs for exact state restoration. Boost your workflow today.

- Repository: [tt-a1i/archify](https://github.com/tt-a1i/archify)
- Tags: how-to-guide
- Published: 2026-08-15

---

**Archify's viewer provides three core navigation primitives—`focus()`, `reach()`, and `deepLink()`—that let you programmatically center the camera on nodes, smoothly bring targets into view, and generate shareable URLs that restore exact view states.**

The `archify` repository implements a lightweight, client-side diagram viewer where all navigation state lives in the browser. These three APIs in `archify/viewer.mjs` give developers precise control over camera positioning and user guidance without requiring server-side coordination.

## Focus: Center and Highlight Specific Nodes

The **`focus()`** method centers the camera on a target node and optionally animates a highlight effect around it. This is the most aggressive navigation option—use it when you want to direct full attention to a specific element.

Under the hood, `focus()` performs four steps:

- Looks up the node's world-space bounding box via `viewer.graph.getNodeBounds(nodeId)`
- Computes a target camera position that centers that box
- Animates the transition using the built-in tween engine (`viewer.tween`)
- Adds a temporary visual highlight layer through `viewer.overlay.addHighlight(nodeId)`

```javascript
// Focus on a node with full animation and highlight
const nodeId = 'product-42';
viewer.focus(nodeId, { animate: true, highlight: true });

```

The method signature in `archify/viewer.mjs` (lines 112-138) accepts an options object with:
- `animate` – whether to tween the camera movement (default: `false`)
- `highlight` – whether to flash a visual indicator around the node (default: `false`)

## Reach: Subtle Navigation That Respects Current View

The **`reach()`** method moves the camera just enough to bring the target node into the visible viewport. Unlike `focus()`, it does not force the node to the center, making it ideal for subtle navigation that preserves the user's spatial context.

The key difference lies in the camera calculation: instead of centering the node, `reach()` computes the minimal translation that makes the node intersect the visible rectangle, respecting an optional `inset` margin in pixels.

```javascript
// Bring a node into view with 30px padding from edges
const nodeId = 'order-7';
viewer.reach(nodeId, { inset: 30 });

```

As implemented in `archify/viewer.mjs` (lines 140-165), this method is particularly useful for:
- Responding to search results without disorienting the user
- Showing related nodes while maintaining the current zoom level
- Progressive disclosure flows where you want gentle camera nudges

## Deep-Linking: Create Permanent, Shareable View URLs

The **`deepLink()`** method generates a URL that encodes the current camera state and selected node identifier. These fragments enable any view state to be reopened later or shared between users.

The serialization process in `archify/viewer.mjs` (lines 167-190) packs:
- The target `nodeId` as the `focus` parameter
- Camera position via `viewer.camera.toJSON()` (x, y coordinates)
- Current zoom level

```javascript
// Generate a shareable URL for the current view
const nodeId = 'customer-3';
const link = viewer.deepLink(nodeId);
console.log('Share this URL:', link);
// Output: https://archify.example.com/viewer.html#focus=customer-3&zoom=2.0&x=845&y=312

```

### Restoring Deep-Linked Views

When a page loads with a hash fragment, `archify/boot.mjs` (lines 45-58) automatically parses and restores the view:

```javascript
// Boot sequence handles fragment restoration automatically
viewer.on('ready', () => {
  const fragment = new URLSearchParams(window.location.hash.slice(1));
  if (fragment.has('focus')) {
    const nodeId = fragment.get('focus');
    viewer.focus(nodeId, { animate: false });
  }
});

```

No server-side storage is required—everything lives in the URL fragment, making deep-linking work with static hosting or embedded deployments.

## Architecture Overview: How Navigation Works Together

All three methods share a common foundation in `archify/camera.mjs`, which abstracts positioning, zoom, and serialization. The camera object is manipulated directly by `focus()` and `reach()`, while `deepLink()` relies on its `toJSON()` method for state persistence.

| Component | Responsibility | Key Methods |
|-----------|--------------|-------------|
| `archify/viewer.mjs` | High-level navigation API | `focus()`, `reach()`, `deepLink()` |
| `archify/camera.mjs` | Position and zoom management | `toJSON()`, `setPosition()`, `setZoom()` |
| `archify/graph.mjs` | Node geometry lookup | `getNodeBounds(nodeId)` |
| `archify/overlay.mjs` | Visual feedback | `addHighlight(nodeId)` |
| `archify/boot.mjs` | URL fragment parsing | Hash detection on initialization |

## Practical Integration Patterns

### Search Result Navigation

Combine `reach()` with search to show results without jarring camera jumps:

```javascript
function onSearchResult(nodeId) {
  // Highlight the result list item
  highlightSearchResult(nodeId);
  // Gently bring node into view
  viewer.reach(nodeId, { inset: 50 });
}

```

### Permalink Generation

Expose `deepLink()` through a share button:

```javascript
document.getElementById('share-btn').addEventListener('click', () => {
  const selectedNode = viewer.selection.active;
  if (selectedNode) {
    const url = viewer.deepLink(selectedNode.id);
    navigator.clipboard.writeText(url);
    showToast('Link copied to clipboard');
  }
});

```

### Initial Load with Context

Pre-focus based on URL parameters when embedding the viewer in dashboards:

```javascript
const params = new URLSearchParams(window.location.search);
const initialNode = params.get('node');
if (initialNode) {
  viewer.once('ready', () => viewer.focus(initialNode, { animate: true }));
}

```

## Summary

- **`focus(nodeId, options)`** – Centers camera on node with optional animation and highlight; use for intentional, attention-directing navigation
- **`reach(nodeId, options)`** – Minimally moves camera to bring node into viewport with configurable inset; use for subtle, context-preserving navigation
- **`deepLink(nodeId)`** – Serializes camera state and node identifier to URL fragment; enables shareable, reproducible views without server dependency

All three APIs are implemented in `archify/viewer.mjs` and depend on `archify/camera.mjs` for state management and `archify/graph.mjs` for geometry queries.

## Frequently Asked Questions

### What is the difference between `focus()` and `reach()` in Archify?

`focus()` centers the target node in the viewport and optionally adds a highlight effect, making it ideal when you want to direct full user attention. `reach()` performs minimal camera movement—just enough to bring the node into view—preserving the user's current spatial context and zoom level. Choose `focus()` for intentional navigation like clicking a search result; use `reach()` for subtle guidance like showing related nodes.

### How does deep-linking work without a server?

Archify encodes all view state directly into the URL hash fragment using `viewer.camera.toJSON()` and the target node identifier. When the page loads, `archify/boot.mjs` parses this fragment and calls `viewer.focus()` to restore the exact camera position. This client-only approach works with static file hosting and requires no database or backend session management.

### Can I disable animation when focusing on a node?

Yes—pass `animate: false` in the options object: `viewer.focus(nodeId, { animate: false })`. This is useful when restoring deep-linked views where you want immediate positioning rather than transitional movement, as implemented in `archify/boot.mjs` during fragment restoration.

### What happens if the node ID passed to `focus()` does not exist?

The `viewer.graph.getNodeBounds(nodeId)` lookup in `archify/viewer.mjs` will return `undefined` for invalid IDs, and the `focus()` method silently returns without modifying the camera. You can check node existence beforehand using `viewer.graph.hasNode(nodeId)` if you need explicit error handling.