@shogo-ai/voice 1.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +30 -0
- package/dist/chunk-D2EVSUZX.js +27 -0
- package/dist/chunk-Q3TJAJ54.js +165 -0
- package/dist/chunk-QHDIEE7O.js +453 -0
- package/dist/chunk-S3S2RNO6.js +671 -0
- package/dist/chunk-ZGYHDC5G.js +423 -0
- package/dist/index.cjs +1316 -0
- package/dist/index.d.cts +432 -0
- package/dist/index.d.ts +432 -0
- package/dist/index.js +735 -0
- package/dist/native/index.cjs +1667 -0
- package/dist/native/index.d.cts +285 -0
- package/dist/native/index.d.ts +285 -0
- package/dist/native/index.js +949 -0
- package/dist/organicParticlesShaders-DIva0vBO.d.cts +693 -0
- package/dist/organicParticlesShaders-DIva0vBO.d.ts +693 -0
- package/dist/react/index.cjs +1670 -0
- package/dist/react/index.d.cts +423 -0
- package/dist/react/index.d.ts +423 -0
- package/dist/react/index.js +952 -0
- package/dist/route/agent.cjs +981 -0
- package/dist/route/agent.d.cts +20 -0
- package/dist/route/agent.d.ts +20 -0
- package/dist/route/agent.js +17 -0
- package/dist/route/audio-tags.cjs +975 -0
- package/dist/route/audio-tags.d.cts +16 -0
- package/dist/route/audio-tags.d.ts +16 -0
- package/dist/route/audio-tags.js +13 -0
- package/dist/route/index.cjs +987 -0
- package/dist/route/index.d.cts +115 -0
- package/dist/route/index.d.ts +115 -0
- package/dist/route/index.js +12 -0
- package/dist/route/signed-url.cjs +975 -0
- package/dist/route/signed-url.d.cts +12 -0
- package/dist/route/signed-url.d.ts +12 -0
- package/dist/route/signed-url.js +13 -0
- package/dist/route/tts-preview.cjs +975 -0
- package/dist/route/tts-preview.d.cts +17 -0
- package/dist/route/tts-preview.d.ts +17 -0
- package/dist/route/tts-preview.js +13 -0
- package/dist/server.cjs +999 -0
- package/dist/server.d.cts +88 -0
- package/dist/server.d.ts +88 -0
- package/dist/server.js +11 -0
- package/dist/types-BKJtOUbE.d.cts +352 -0
- package/dist/types-BKJtOUbE.d.ts +352 -0
- package/package.json +148 -0
|
@@ -0,0 +1,693 @@
|
|
|
1
|
+
import { UIMessage } from 'ai';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Shared option / result types for `useVoiceConversation` across
|
|
5
|
+
* platforms. The web hook ([packages/sdk/src/voice/react]) and the
|
|
6
|
+
* native hook ([packages/sdk/src/voice/native]) both export their own
|
|
7
|
+
* `UseVoiceConversationOptions` / `UseVoiceConversationResult`
|
|
8
|
+
* types — typically by re-exporting `BaseVoiceConversationOptions` /
|
|
9
|
+
* `BaseVoiceConversationResult` from this module — so consumers can
|
|
10
|
+
* import them from whichever subpath they're using.
|
|
11
|
+
*/
|
|
12
|
+
interface BaseVoiceConversationOptions {
|
|
13
|
+
/** Name of the companion the user is talking to (passed as a dynamic variable). */
|
|
14
|
+
characterName: string;
|
|
15
|
+
/** Server path that mints an 11Labs signed URL for this user. Default: `/api/voice/signed-url`. */
|
|
16
|
+
signedUrlPath?: string;
|
|
17
|
+
/** Server path the built-in `add_memory` tool POSTs to. Default: `/api/memory/add`. */
|
|
18
|
+
memoryAddPath?: string;
|
|
19
|
+
/** Server path used to auto-inject memory context. Default: `/api/memory/retrieve`. */
|
|
20
|
+
memoryRetrievePath?: string;
|
|
21
|
+
/** Server path the transcript is POSTed to when the session ends. Default: `/api/memory/ingest`. */
|
|
22
|
+
transcriptIngestPath?: string;
|
|
23
|
+
/** Disable the built-in memory context auto-injection. Default: `true` (enabled). */
|
|
24
|
+
autoInjectMemory?: boolean;
|
|
25
|
+
/** Disable the built-in `add_memory` client tool. Default: `true` (enabled). */
|
|
26
|
+
includeMemoryTool?: boolean;
|
|
27
|
+
/**
|
|
28
|
+
* Additional client tools merged onto `add_memory`. Tools defined here with
|
|
29
|
+
* the name `add_memory` override the built-in implementation.
|
|
30
|
+
*/
|
|
31
|
+
clientTools?: Record<string, (params: Record<string, unknown>) => Promise<string> | string>;
|
|
32
|
+
/**
|
|
33
|
+
* Called once per session with the accumulated transcript when the session
|
|
34
|
+
* disconnects. Defaults to POST `transcriptIngestPath`; set this to suppress
|
|
35
|
+
* or replace that behaviour.
|
|
36
|
+
*/
|
|
37
|
+
onTranscript?: (transcript: string) => void | Promise<void>;
|
|
38
|
+
/**
|
|
39
|
+
* Credentials mode for built-in fetch calls.
|
|
40
|
+
*
|
|
41
|
+
* - Defaults to `same-origin` in the legacy cookie path.
|
|
42
|
+
* - Defaults to `omit` when `shogoApiKey` is provided (bearer auth is
|
|
43
|
+
* self-contained and you don't want browsers to attach unrelated
|
|
44
|
+
* third-party cookies cross-origin).
|
|
45
|
+
* - Explicit values always win — pass `'include'` to carry both a cookie
|
|
46
|
+
* and the bearer in a hybrid same-origin app.
|
|
47
|
+
*/
|
|
48
|
+
fetchCredentials?: RequestCredentials;
|
|
49
|
+
/**
|
|
50
|
+
* Shogo API key (`shogo_sk_*`) used to authenticate voice routes. When
|
|
51
|
+
* provided, the hook attaches `Authorization: Bearer <key>` to the
|
|
52
|
+
* signed-URL fetch and memory fetches, and appends `?projectId=` to
|
|
53
|
+
* the signed-URL path. Either `shogoApiKey` or a session cookie must
|
|
54
|
+
* be resolvable by the server — both may be passed; the server's
|
|
55
|
+
* `apiKeyOrSession` middleware picks bearer first.
|
|
56
|
+
*/
|
|
57
|
+
shogoApiKey?: string;
|
|
58
|
+
/**
|
|
59
|
+
* Project id (Shogo's `projectId`). Required alongside `shogoApiKey`
|
|
60
|
+
* for Mode B voice. The server uses it to look up / provision the
|
|
61
|
+
* project's ElevenLabs agent and resolve the workspace.
|
|
62
|
+
*/
|
|
63
|
+
projectId?: string;
|
|
64
|
+
/**
|
|
65
|
+
* Optional named agent to talk to. Forwarded as `?agentName=` on
|
|
66
|
+
* the signed-URL fetch and resolved server-side to a project-scoped
|
|
67
|
+
* `ProjectAgent` row (created via `shogo deploy`). When omitted,
|
|
68
|
+
* the server resolves the project's `default` agent.
|
|
69
|
+
*
|
|
70
|
+
* The same `agentName` reaches the same persona via
|
|
71
|
+
* `useChatConversation({ agentName })` — voice and chat are two
|
|
72
|
+
* transports for one agent record.
|
|
73
|
+
*/
|
|
74
|
+
agentName?: string;
|
|
75
|
+
/** Called on connection errors. */
|
|
76
|
+
onError?: (error: unknown) => void;
|
|
77
|
+
/** Called on each message (user or agent) for debugging / custom UI. */
|
|
78
|
+
onMessage?: (message: {
|
|
79
|
+
source: string;
|
|
80
|
+
message: string;
|
|
81
|
+
}) => void;
|
|
82
|
+
/**
|
|
83
|
+
* Stable conversation id used to correlate this voice session with
|
|
84
|
+
* a sibling text thread (see `BaseChatConversationOptions.conversationId`).
|
|
85
|
+
*
|
|
86
|
+
* When set, forwarded to ElevenLabs as a `conversation_id` dynamic
|
|
87
|
+
* variable so the agent's prompt can reference it (`{{conversation_id}}`),
|
|
88
|
+
* and surfaced on `BaseVoiceConversationResult.conversationId` so a
|
|
89
|
+
* single bridge component can read the same value from either hook.
|
|
90
|
+
*
|
|
91
|
+
* Independent of the convai-side conversation id — see
|
|
92
|
+
* `BaseVoiceConversationResult.convaiConversationId` for that. The
|
|
93
|
+
* caller-supplied value wins on `result.conversationId` so consumers
|
|
94
|
+
* who keep their own thread store don't have to special-case the
|
|
95
|
+
* default. Optional; when omitted, `result.conversationId` mirrors
|
|
96
|
+
* the convai conversation id once the session connects.
|
|
97
|
+
*/
|
|
98
|
+
conversationId?: string;
|
|
99
|
+
/**
|
|
100
|
+
* Extra dynamic variables forwarded to ElevenLabs at session start.
|
|
101
|
+
* Merged on top of the SDK's built-in variables — `character_name`,
|
|
102
|
+
* `user_context`, and `conversation_id` (when supplied) always win
|
|
103
|
+
* on collision so consumers can't accidentally override them.
|
|
104
|
+
*
|
|
105
|
+
* Typical use: surface per-user fields from the consumer's own
|
|
106
|
+
* `Companion` row (display name, relationship stage, custom greeting
|
|
107
|
+
* tokens) so the agent's prompt can reference them via
|
|
108
|
+
* `{{var_name}}`. Variables also need to be declared in the agent's
|
|
109
|
+
* `dynamic_variable_placeholders` (set at deploy time) for EL to
|
|
110
|
+
* pick up the value.
|
|
111
|
+
*
|
|
112
|
+
* Non-string values are coerced via `String(...)` before being
|
|
113
|
+
* forwarded; `null` / `undefined` values are silently dropped.
|
|
114
|
+
*/
|
|
115
|
+
dynamicVariables?: Record<string, unknown> | null;
|
|
116
|
+
}
|
|
117
|
+
interface BaseVoiceConversationResult {
|
|
118
|
+
/**
|
|
119
|
+
* Begin a new session. Requests microphone permission and fetches a
|
|
120
|
+
* signed URL.
|
|
121
|
+
*
|
|
122
|
+
* `suppressFirstMessage` mirrors the option on `restart()` — when
|
|
123
|
+
* `true`, the new session is opened with `overrides.agent.firstMessage = ''`
|
|
124
|
+
* so the agent skips its opening greeting. Useful when the consumer
|
|
125
|
+
* is programmatically reconnecting (e.g. to deliver a one-shot spoken
|
|
126
|
+
* summary) and does not want to hear the configured intro.
|
|
127
|
+
*/
|
|
128
|
+
start: (options?: {
|
|
129
|
+
suppressFirstMessage?: boolean;
|
|
130
|
+
}) => Promise<void>;
|
|
131
|
+
/** End the current session (if any). */
|
|
132
|
+
end: () => void;
|
|
133
|
+
/**
|
|
134
|
+
* Tear down the active session and immediately reconnect. Primarily
|
|
135
|
+
* used as a programmatic barge-in: ElevenLabs doesn't expose a public
|
|
136
|
+
* `interrupt()` on `useConversation`, so stopping the agent mid-
|
|
137
|
+
* utterance requires a fast disconnect + reconnect. When
|
|
138
|
+
* `suppressFirstMessage` is `true` (the default), the new session is
|
|
139
|
+
* started with `overrides.agent.firstMessage = ''` so the agent does
|
|
140
|
+
* not replay its intro. The accumulated transcript is preserved
|
|
141
|
+
* across the reconnect gap.
|
|
142
|
+
*/
|
|
143
|
+
restart: (options?: {
|
|
144
|
+
suppressFirstMessage?: boolean;
|
|
145
|
+
}) => Promise<void>;
|
|
146
|
+
/** `'disconnected' | 'connecting' | 'connected'`. */
|
|
147
|
+
status: 'disconnected' | 'connecting' | 'connected';
|
|
148
|
+
/** Whether the agent is currently speaking. */
|
|
149
|
+
isSpeaking: boolean;
|
|
150
|
+
/** Whether the agent is currently listening. */
|
|
151
|
+
isListening: boolean;
|
|
152
|
+
/** Whether the microphone is currently muted (input audio is gated off). */
|
|
153
|
+
isMuted: boolean;
|
|
154
|
+
/**
|
|
155
|
+
* Toggle the microphone mute state without tearing down the session.
|
|
156
|
+
* Wraps `@elevenlabs/react` (or `@elevenlabs/react-native`)'s
|
|
157
|
+
* `setMuted`, which in turn calls `conversation.setMicMuted()`.
|
|
158
|
+
* Muting does not end the WebSocket or release the underlying media
|
|
159
|
+
* stream; it simply stops input audio from being forwarded to the
|
|
160
|
+
* server.
|
|
161
|
+
*/
|
|
162
|
+
setMuted: (isMuted: boolean) => void;
|
|
163
|
+
/** For consumers who want to drive their own visualisation / lipsync. */
|
|
164
|
+
getOutputByteFrequencyData: () => Uint8Array | null;
|
|
165
|
+
/** Imperatively send a contextual update (e.g. "user navigated to X"). */
|
|
166
|
+
sendContextualUpdate: (text: string) => void;
|
|
167
|
+
/**
|
|
168
|
+
* Imperatively inject a user-role message, forcing the agent to take
|
|
169
|
+
* its next turn immediately (rather than waiting for the human to
|
|
170
|
+
* speak). Useful for out-of-band prompts like "please summarise what
|
|
171
|
+
* just happened". The injected text is *not* spoken aloud — the agent
|
|
172
|
+
* treats it as if the user said it.
|
|
173
|
+
*/
|
|
174
|
+
sendUserMessage: (text: string) => void;
|
|
175
|
+
/**
|
|
176
|
+
* Low-level signal that the user is active (typing, clicking) so the
|
|
177
|
+
* agent doesn't try to fill silence. Used as a fallback nudge when we
|
|
178
|
+
* want to keep the session "warm" without forcing a full turn.
|
|
179
|
+
*/
|
|
180
|
+
sendUserActivity: () => void;
|
|
181
|
+
/**
|
|
182
|
+
* Stable conversation id for cross-transport correlation. Returns the
|
|
183
|
+
* caller-supplied `BaseVoiceConversationOptions.conversationId` when
|
|
184
|
+
* one was provided; otherwise mirrors the convai conversation id once
|
|
185
|
+
* the session connects (and is `null` while disconnected).
|
|
186
|
+
*
|
|
187
|
+
* Pass this same value to `useChatConversation({ conversationId })`
|
|
188
|
+
* to stitch a text thread onto the same logical conversation.
|
|
189
|
+
*/
|
|
190
|
+
conversationId: string | null;
|
|
191
|
+
/**
|
|
192
|
+
* The convai-side conversation id reported by ElevenLabs on connect.
|
|
193
|
+
* Always reflects the underlying transport id (or `null` while
|
|
194
|
+
* disconnected) regardless of whether the consumer supplied an id.
|
|
195
|
+
* Useful for log correlation against ElevenLabs dashboards even
|
|
196
|
+
* when the consumer is using their own ids on `conversationId`.
|
|
197
|
+
*/
|
|
198
|
+
convaiConversationId: string | null;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Shared option / result types for `useChatConversation` across
|
|
203
|
+
* platforms.
|
|
204
|
+
*
|
|
205
|
+
* The text-chat hook is the audio-free sibling of
|
|
206
|
+
* `useVoiceConversation` (see `./types.ts`): it talks to the same
|
|
207
|
+
* underlying agent persona, reuses the same auth surface
|
|
208
|
+
* (`shogoApiKey` + `projectId` or session cookie), and exposes the
|
|
209
|
+
* same client-tool registration shape — but the wire transport is a
|
|
210
|
+
* plain streaming HTTP POST instead of an ElevenLabs Convai WebSocket.
|
|
211
|
+
*
|
|
212
|
+
* Why a separate `Base*ChatConversation*` instead of overloading the
|
|
213
|
+
* voice option types: the audio-vs-text wire differences (no
|
|
214
|
+
* `signedUrlPath`, no transcript persistence flush, no
|
|
215
|
+
* `firstMessage`/`characterName` dynamic variables, no `setMuted`,
|
|
216
|
+
* etc.) are large enough that conflating them produces a worse public
|
|
217
|
+
* API. Instead, both `Base*` shapes deliberately share the auth /
|
|
218
|
+
* `projectId` / `conversationId` fields verbatim so consumers can
|
|
219
|
+
* thread a single config object through both hooks in a "shared
|
|
220
|
+
* agent, two transports" app — see `packages/sdk/README.md` §"Voice
|
|
221
|
+
* + text bridge" for the canonical pattern.
|
|
222
|
+
*
|
|
223
|
+
* Web (`packages/sdk/src/voice/react/useChatConversation.ts`) and
|
|
224
|
+
* native (`packages/sdk/src/voice/native/useChatConversation.ts`) both
|
|
225
|
+
* re-export `BaseChatConversation*` as their own concrete option /
|
|
226
|
+
* result types so consumers can import whichever subpath matches
|
|
227
|
+
* their platform without crossing the platform boundary.
|
|
228
|
+
*/
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Tool implementation surface accepted by `useChatConversation`.
|
|
232
|
+
* Mirrors `ClientToolFn` in `./memory.ts` (which the voice hook uses)
|
|
233
|
+
* so the same tool map drops cleanly into both hooks.
|
|
234
|
+
*
|
|
235
|
+
* Tools are resolved client-side: the server route declares each tool
|
|
236
|
+
* WITHOUT an `execute` function, the model produces a tool-call event,
|
|
237
|
+
* and the hook calls into this map and POSTs the result back via
|
|
238
|
+
* `addToolOutput`. Any returned string is forwarded to the agent as
|
|
239
|
+
* the tool result; thrown errors are surfaced as `output-error`.
|
|
240
|
+
*/
|
|
241
|
+
type ChatClientToolFn = (params: Record<string, unknown>) => string | Promise<string>;
|
|
242
|
+
/** Status surface mirroring `@ai-sdk/react`'s `useChat().status`. */
|
|
243
|
+
type ChatConversationStatus = 'ready' | 'submitted' | 'streaming' | 'error';
|
|
244
|
+
/**
|
|
245
|
+
* Per-tool wire spec sent to the server as part of the request body.
|
|
246
|
+
*
|
|
247
|
+
* The server re-declares each tool to the model so it can produce
|
|
248
|
+
* tool-call events. Unlike voice (where the agent is provisioned with
|
|
249
|
+
* its tools server-side once), text consumers register tools per
|
|
250
|
+
* client because the same agent prompt may want to drive different
|
|
251
|
+
* client-resident effects on different surfaces. We accept a
|
|
252
|
+
* JSON-Schema-shaped descriptor here — the SDK does not depend on
|
|
253
|
+
* `zod` so consumers are free to write the schemas inline.
|
|
254
|
+
*
|
|
255
|
+
* The server validates the descriptor before passing it to the model
|
|
256
|
+
* and rejects unknown shapes; see `apps/api/src/routes/chat.ts`.
|
|
257
|
+
*/
|
|
258
|
+
interface ChatToolDescriptor {
|
|
259
|
+
/** Stable tool name (must match the key under `clientTools`). */
|
|
260
|
+
name: string;
|
|
261
|
+
/** Free-form description handed to the model. */
|
|
262
|
+
description: string;
|
|
263
|
+
/**
|
|
264
|
+
* JSON Schema for the tool's input. Keep this small — long schemas
|
|
265
|
+
* inflate every turn's prompt. `type: 'object'` with a flat
|
|
266
|
+
* `properties` map covers ~all real cases.
|
|
267
|
+
*/
|
|
268
|
+
inputSchema: Record<string, unknown>;
|
|
269
|
+
}
|
|
270
|
+
interface BaseChatConversationOptions {
|
|
271
|
+
/**
|
|
272
|
+
* Streaming chat endpoint URL. Defaults to `'/api/chat/turn'` (the
|
|
273
|
+
* route shipped in `apps/api`). Override to point at a custom
|
|
274
|
+
* deployment or to embed query params other than `projectId` /
|
|
275
|
+
* `conversationId`.
|
|
276
|
+
*
|
|
277
|
+
* The hook appends `?projectId=` and `?conversationId=` to whatever
|
|
278
|
+
* value is supplied here, so a `?foo=bar` already on the URL is
|
|
279
|
+
* preserved.
|
|
280
|
+
*/
|
|
281
|
+
api?: string;
|
|
282
|
+
/**
|
|
283
|
+
* Shogo API key (`shogo_sk_*`). When set, attached as
|
|
284
|
+
* `Authorization: Bearer <key>` on every request and
|
|
285
|
+
* `?projectId=` is appended. Mirrors the option of the same name
|
|
286
|
+
* on `BaseVoiceConversationOptions`.
|
|
287
|
+
*/
|
|
288
|
+
shogoApiKey?: string;
|
|
289
|
+
/**
|
|
290
|
+
* Project id. Required when `shogoApiKey` is set so the server can
|
|
291
|
+
* resolve the project's persona prompt, memory, and tool allowlist.
|
|
292
|
+
* Mirrors `BaseVoiceConversationOptions.projectId`.
|
|
293
|
+
*/
|
|
294
|
+
projectId?: string;
|
|
295
|
+
/**
|
|
296
|
+
* Optional named agent to converse with. Appended to the chat URL
|
|
297
|
+
* as `?agentName=` and resolved server-side to a project-scoped
|
|
298
|
+
* `ProjectAgent` row whose `systemPrompt` / `model` / tool
|
|
299
|
+
* allowlist drive this turn. When omitted, the server resolves the
|
|
300
|
+
* project's `default` agent (or falls back to its built-in
|
|
301
|
+
* persona for projects predating the agents table).
|
|
302
|
+
*
|
|
303
|
+
* The same `agentName` reaches the same persona via
|
|
304
|
+
* `useVoiceConversation({ agentName })` — voice and chat are two
|
|
305
|
+
* transports for one agent record.
|
|
306
|
+
*/
|
|
307
|
+
agentName?: string;
|
|
308
|
+
/**
|
|
309
|
+
* Stable conversation id used by the consumer to correlate this
|
|
310
|
+
* text thread with a sibling voice session (see
|
|
311
|
+
* `BaseVoiceConversationResult.conversationId`). The value is
|
|
312
|
+
* forwarded to the server in the request body so durable transcripts
|
|
313
|
+
* land under the same row, and is appended to the URL as
|
|
314
|
+
* `?conversationId=` for log correlation.
|
|
315
|
+
*
|
|
316
|
+
* Optional — if omitted, the server treats each turn as a free-
|
|
317
|
+
* standing thread (state-via-`messages`-array). Most consumers want
|
|
318
|
+
* to set this to keep voice + text under one logical conversation.
|
|
319
|
+
*/
|
|
320
|
+
conversationId?: string;
|
|
321
|
+
/**
|
|
322
|
+
* Initial messages used to seed the in-memory thread on first
|
|
323
|
+
* mount. Useful for cross-modality merging (e.g. seed with the
|
|
324
|
+
* voice transcript before opening text), prefilling a greeting, or
|
|
325
|
+
* resuming a stored thread. Forwarded to `useChat({ messages })`.
|
|
326
|
+
*
|
|
327
|
+
* Subsequent updates should go through `setMessages(...)` on the
|
|
328
|
+
* returned hook surface — changing `initialMessages` after first
|
|
329
|
+
* render does NOT replay them.
|
|
330
|
+
*/
|
|
331
|
+
initialMessages?: UIMessage[];
|
|
332
|
+
/**
|
|
333
|
+
* Client-side tool implementations keyed by tool name. The
|
|
334
|
+
* companion `tools` descriptor list is what the server registers
|
|
335
|
+
* with the model on each turn; the keys must match.
|
|
336
|
+
*/
|
|
337
|
+
clientTools?: Record<string, ChatClientToolFn>;
|
|
338
|
+
/**
|
|
339
|
+
* Tool descriptors forwarded to the server in the request body.
|
|
340
|
+
* Empty / undefined ships the request without any tools (the
|
|
341
|
+
* server's default agent persona still runs, just with no tool
|
|
342
|
+
* surface). Required when `clientTools` is non-empty — the server
|
|
343
|
+
* needs both halves to declare the tool to the model.
|
|
344
|
+
*/
|
|
345
|
+
tools?: ChatToolDescriptor[];
|
|
346
|
+
/**
|
|
347
|
+
* Credentials mode for `fetch`. Defaults to `'same-origin'` on
|
|
348
|
+
* web (matches `useShogoVoice`), `'omit'` on native when
|
|
349
|
+
* `shogoApiKey` is set or `'include'` otherwise (matches
|
|
350
|
+
* `useShogoVoice` native defaults).
|
|
351
|
+
*/
|
|
352
|
+
fetchCredentials?: RequestCredentials;
|
|
353
|
+
/**
|
|
354
|
+
* Stable id forwarded to `useChat({ id })`. Only used to keep the
|
|
355
|
+
* AI-SDK chat instance keyed across re-renders / route changes; not
|
|
356
|
+
* sent on the wire. Defaults to a value derived from
|
|
357
|
+
* `conversationId` (or a per-mount fallback).
|
|
358
|
+
*/
|
|
359
|
+
id?: string;
|
|
360
|
+
/** Called on transport / model errors. */
|
|
361
|
+
onError?: (error: unknown) => void;
|
|
362
|
+
/**
|
|
363
|
+
* Per-user dynamic variables forwarded with each chat turn. Mirrors
|
|
364
|
+
* the option of the same name on `BaseVoiceConversationOptions` so a
|
|
365
|
+
* single config object can drive both hooks.
|
|
366
|
+
*
|
|
367
|
+
* V1 plumbs this through to the request body — server-side rendering
|
|
368
|
+
* into the system prompt is not yet wired up. Treat it as forwards-
|
|
369
|
+
* compatible: setting the value today is safe and lets the server
|
|
370
|
+
* pick it up once support lands without a SDK bump.
|
|
371
|
+
*/
|
|
372
|
+
dynamicVariables?: Record<string, unknown> | null;
|
|
373
|
+
}
|
|
374
|
+
interface BaseChatConversationResult {
|
|
375
|
+
/** Live message list mirroring `useChat().messages`. */
|
|
376
|
+
messages: UIMessage[];
|
|
377
|
+
/**
|
|
378
|
+
* Send a new user turn. Resolves once the request leaves the
|
|
379
|
+
* client; the returned promise does NOT wait for the assistant
|
|
380
|
+
* stream to finish — observe `messages` / `status` for that.
|
|
381
|
+
*/
|
|
382
|
+
sendMessage: (text: string) => Promise<void>;
|
|
383
|
+
/**
|
|
384
|
+
* Replace the current in-memory thread. Useful for hydrating from
|
|
385
|
+
* a persisted store, seeding with a sibling voice transcript, or
|
|
386
|
+
* surgically rewriting an earlier turn. Forwarded directly to
|
|
387
|
+
* `useChat().setMessages`.
|
|
388
|
+
*/
|
|
389
|
+
setMessages: (messages: UIMessage[]) => void;
|
|
390
|
+
/**
|
|
391
|
+
* Append a synthetic assistant message without making a model
|
|
392
|
+
* call. Use for prefilling a deterministic first-turn greeting,
|
|
393
|
+
* surfacing a heartbeat from a sibling voice session as if the
|
|
394
|
+
* agent had spoken it, etc. Generates an id locally; the message
|
|
395
|
+
* is NOT echoed to the server.
|
|
396
|
+
*/
|
|
397
|
+
appendAssistantMessage: (text: string) => void;
|
|
398
|
+
/**
|
|
399
|
+
* Append a synthetic user message without dispatching a turn.
|
|
400
|
+
* Symmetric counterpart to `appendAssistantMessage` — useful for
|
|
401
|
+
* merging a voice transcript turn into the text thread without
|
|
402
|
+
* round-tripping through the model.
|
|
403
|
+
*/
|
|
404
|
+
appendUserMessage: (text: string) => void;
|
|
405
|
+
/** Current request lifecycle. See `ChatConversationStatus`. */
|
|
406
|
+
status: ChatConversationStatus;
|
|
407
|
+
/**
|
|
408
|
+
* The `conversationId` the hook is currently using. Returns the
|
|
409
|
+
* caller-supplied option verbatim, or `null` when none was set.
|
|
410
|
+
* Provided so a single bridge component can read the value from
|
|
411
|
+
* either hook without juggling default-vs-override logic.
|
|
412
|
+
*/
|
|
413
|
+
conversationId: string | null;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
/**
|
|
417
|
+
* Configuration surface for the `OrganicSphere` audio-reactive
|
|
418
|
+
* visualization. All values are plain numbers / strings / null so the
|
|
419
|
+
* whole object is JSON-serializable — save it, transport it, paste it
|
|
420
|
+
* into a preset library.
|
|
421
|
+
*
|
|
422
|
+
* The sphere renders four eased "variations" driven by frequency bands:
|
|
423
|
+
*
|
|
424
|
+
* low → modulates time-evolution speed (drift + noise phase)
|
|
425
|
+
* volume → drives `uDisplacementStrength` (how far each vertex pushes
|
|
426
|
+
* out from the sphere surface)
|
|
427
|
+
* medium → drives `uFresnelMultiplier` (how bright the lighting pass
|
|
428
|
+
* gets near silhouette edges)
|
|
429
|
+
* high → drives `uDistortionStrength` (large-scale warping of the
|
|
430
|
+
* underlying position field before displacement)
|
|
431
|
+
*
|
|
432
|
+
* Each variation has a resting `idle` value used when audio is off, a
|
|
433
|
+
* `gain` multiplier applied to the band level, a `floor` minimum while
|
|
434
|
+
* audio is playing, and separate attack / decay easing rates.
|
|
435
|
+
*
|
|
436
|
+
* Use `resolveOrganicSphereConfig(partial)` to merge a partial override
|
|
437
|
+
* into the defaults; `OrganicSphere` does this internally so consumers
|
|
438
|
+
* can pass any subset.
|
|
439
|
+
*/
|
|
440
|
+
interface BandReactivity {
|
|
441
|
+
/** Resting target used when no audio source is connected. */
|
|
442
|
+
idle: number;
|
|
443
|
+
/** Multiplier applied to the normalized band level (0–1) while audio plays. */
|
|
444
|
+
gain: number;
|
|
445
|
+
/** Minimum target while audio is playing, before easing. */
|
|
446
|
+
floor: number;
|
|
447
|
+
/**
|
|
448
|
+
* Easing rate when the target rises above `current` (snappier attack
|
|
449
|
+
* at higher values; per-ms-of-frame delta, not per-frame).
|
|
450
|
+
*/
|
|
451
|
+
attack: number;
|
|
452
|
+
/** Easing rate when the target falls below `current` (per-ms). */
|
|
453
|
+
decay: number;
|
|
454
|
+
}
|
|
455
|
+
interface OrganicSphereConfig {
|
|
456
|
+
/** Color of light A (default is a deep Shogo burnt orange). */
|
|
457
|
+
lightAColor: string;
|
|
458
|
+
/** Color of light B (default is a warm Shogo amber). */
|
|
459
|
+
lightBColor: string;
|
|
460
|
+
/** Intensity multiplier for light A. Higher = brighter contribution. */
|
|
461
|
+
lightAIntensity: number;
|
|
462
|
+
/** Intensity multiplier for light B. */
|
|
463
|
+
lightBIntensity: number;
|
|
464
|
+
/**
|
|
465
|
+
* Rim-highlight color. `null` falls back to `lightBColor`, which keeps
|
|
466
|
+
* the silhouette monochromatic and impossible to wash out to white.
|
|
467
|
+
*/
|
|
468
|
+
rimColor: string | null;
|
|
469
|
+
/**
|
|
470
|
+
* Fresnel value above which the rim starts contributing. Higher →
|
|
471
|
+
* tighter rim; lower → rim bleeds across more of the surface.
|
|
472
|
+
*/
|
|
473
|
+
rimThreshold: number;
|
|
474
|
+
/** Exponent on the rim falloff curve. Higher → sharper edge. */
|
|
475
|
+
rimPower: number;
|
|
476
|
+
/** Spatial frequency of the distortion noise (`uDistortionFrequency`). */
|
|
477
|
+
distortionFrequency: number;
|
|
478
|
+
/** Spatial frequency of the displacement noise (`uDisplacementFrequency`). */
|
|
479
|
+
displacementFrequency: number;
|
|
480
|
+
/** Fresnel offset — shifts where the rim and lighting gradient peak. */
|
|
481
|
+
fresnelOffset: number;
|
|
482
|
+
/** Fresnel power — steepens or softens the lighting gradient. */
|
|
483
|
+
fresnelPower: number;
|
|
484
|
+
/**
|
|
485
|
+
* Volume variation → `uDisplacementStrength`. Driven by the loudest
|
|
486
|
+
* of the first three bands (`max(l0, l1, l2)`).
|
|
487
|
+
*/
|
|
488
|
+
volume: BandReactivity;
|
|
489
|
+
/** Mid-band variation → `uFresnelMultiplier`. Driven by `l1`. */
|
|
490
|
+
medium: BandReactivity;
|
|
491
|
+
/** High-band variation → `uDistortionStrength`. Driven by `l2`. */
|
|
492
|
+
high: BandReactivity;
|
|
493
|
+
/**
|
|
494
|
+
* Low-band variation → time evolution speed (how fast the noise
|
|
495
|
+
* offset scrolls). Driven by `l0`.
|
|
496
|
+
*/
|
|
497
|
+
low: BandReactivity;
|
|
498
|
+
/** Camera z-position. Larger = more zoomed out. */
|
|
499
|
+
cameraZ: number;
|
|
500
|
+
/** Vertical field-of-view in degrees. */
|
|
501
|
+
fov: number;
|
|
502
|
+
/**
|
|
503
|
+
* Seed used to derive the initial "wander" offset phase, so identical
|
|
504
|
+
* audio produces identical visuals across playbacks. Any integer is
|
|
505
|
+
* fine; change it to roll a different starting pose. When `active`
|
|
506
|
+
* flips from `false` → `true`, the sphere resets to this phase.
|
|
507
|
+
*/
|
|
508
|
+
seed: number;
|
|
509
|
+
/**
|
|
510
|
+
* Sphere mesh subdivisions. Higher = smoother displacement, more GPU
|
|
511
|
+
* cost. Changing at runtime triggers a scene rebuild.
|
|
512
|
+
*/
|
|
513
|
+
subdivisions: number;
|
|
514
|
+
/**
|
|
515
|
+
* Device-pixel-ratio cap. `null` picks `min(devicePixelRatio, 2)`.
|
|
516
|
+
* Set to `1` for lower-end devices.
|
|
517
|
+
*/
|
|
518
|
+
maxPixelRatio: number | null;
|
|
519
|
+
/**
|
|
520
|
+
* Scene clear color. `null` keeps the canvas fully transparent so the
|
|
521
|
+
* sphere blends into the host UI.
|
|
522
|
+
*/
|
|
523
|
+
backgroundColor: string | null;
|
|
524
|
+
}
|
|
525
|
+
/**
|
|
526
|
+
* Preset tuned for Shogo's Voice Mode hero — warm Shogo orange palette,
|
|
527
|
+
* rim tied to `lightBColor`, camera zoomed back so the sphere reads
|
|
528
|
+
* "contained" rather than "looming", reactivity values copied from
|
|
529
|
+
* Bruno Simon's upstream `organic-sphere` demo so silhouettes animate
|
|
530
|
+
* faithfully.
|
|
531
|
+
*/
|
|
532
|
+
declare const DEFAULT_ORGANIC_SPHERE_CONFIG: OrganicSphereConfig;
|
|
533
|
+
/**
|
|
534
|
+
* Merge a partial override into `DEFAULT_ORGANIC_SPHERE_CONFIG`, doing a
|
|
535
|
+
* shallow merge on the root + per-band merge on `volume/medium/high/low`
|
|
536
|
+
* so consumers only need to specify what they want to change.
|
|
537
|
+
*/
|
|
538
|
+
declare function resolveOrganicSphereConfig(partial?: Partial<OrganicSphereConfig>): OrganicSphereConfig;
|
|
539
|
+
|
|
540
|
+
/**
|
|
541
|
+
* Configuration surface for the `OrganicParticles` audio-reactive
|
|
542
|
+
* visualization — a particle-cloud sibling of `OrganicSphere`.
|
|
543
|
+
*
|
|
544
|
+
* Shares the same `BandReactivity` shape and four-band model
|
|
545
|
+
* (volume / medium / high / low) as the sphere, so audio-tuning muscle
|
|
546
|
+
* memory carries over. What changes is the cloud-specific fields:
|
|
547
|
+
*
|
|
548
|
+
* volume → radial displacement (how far the cloud bursts outward)
|
|
549
|
+
* medium → particle size boost (how puffy each point renders)
|
|
550
|
+
* high → swirl strength (how much the cloud twists)
|
|
551
|
+
* low → time evolution speed (noise phase drift)
|
|
552
|
+
*
|
|
553
|
+
* Like `OrganicSphereConfig`, everything is JSON-serializable.
|
|
554
|
+
*/
|
|
555
|
+
|
|
556
|
+
interface OrganicParticlesConfig {
|
|
557
|
+
/**
|
|
558
|
+
* Number of particles. Changing this rebuilds the scene. Sweet spot
|
|
559
|
+
* for visual density without frame-drop is 20k–80k on a modern GPU.
|
|
560
|
+
*/
|
|
561
|
+
count: number;
|
|
562
|
+
/**
|
|
563
|
+
* Inner / outer radius of the seeded spawn shell. Particles are
|
|
564
|
+
* uniformly distributed between these two radii so the cloud has
|
|
565
|
+
* perceptible thickness rather than sitting on a single sphere.
|
|
566
|
+
*/
|
|
567
|
+
innerRadius: number;
|
|
568
|
+
outerRadius: number;
|
|
569
|
+
/** Spatial frequency of the Perlin noise warp. */
|
|
570
|
+
noiseFrequency: number;
|
|
571
|
+
/** Strength of the noise warp. */
|
|
572
|
+
noiseStrength: number;
|
|
573
|
+
/**
|
|
574
|
+
* Constant per-ms Y-axis rotation applied to the whole cloud,
|
|
575
|
+
* independent of audio. `0` (default) means the cloud is perfectly
|
|
576
|
+
* still when no audio plays. Positive = spin right, negative = left.
|
|
577
|
+
*/
|
|
578
|
+
rotationBias: number;
|
|
579
|
+
/**
|
|
580
|
+
* Rate (per ms) at which `uTime` advances the per-particle swirl
|
|
581
|
+
* angle in the shader, independent of audio. `0` (default) = swirl
|
|
582
|
+
* only responds to audio high-band + particle height.
|
|
583
|
+
*/
|
|
584
|
+
swirlTimeRate: number;
|
|
585
|
+
/**
|
|
586
|
+
* Rate (per ms) at which `uTime` advances while the cloud is idle
|
|
587
|
+
* (i.e. `active === false`). Lets the Perlin noise field keep
|
|
588
|
+
* shimmering subtly when nothing is playing so the cloud doesn't feel
|
|
589
|
+
* dead. `0` = fully frozen when paused. Default `0.0001` ≈ one full
|
|
590
|
+
* noise period every ~10 s — perceptible but not distracting.
|
|
591
|
+
*/
|
|
592
|
+
idleNoiseRate: number;
|
|
593
|
+
/** Base particle size (before audio boost / perspective scaling). */
|
|
594
|
+
sizeBase: number;
|
|
595
|
+
/**
|
|
596
|
+
* Edge softness of each particle sprite, in [0, 1]. `0` = hard disc,
|
|
597
|
+
* `1` = heavy feather/plasma. ~0.6 reads as "nebula".
|
|
598
|
+
*/
|
|
599
|
+
softness: number;
|
|
600
|
+
/** Overall opacity multiplier. */
|
|
601
|
+
opacity: number;
|
|
602
|
+
/** Color at the cloud core (low `vHeat`). */
|
|
603
|
+
colorCool: string;
|
|
604
|
+
/** Color at the cloud fringe (high `vHeat`). */
|
|
605
|
+
colorHot: string;
|
|
606
|
+
/** volume → radial displacement push. */
|
|
607
|
+
volume: BandReactivity;
|
|
608
|
+
/** medium → per-particle size boost. */
|
|
609
|
+
medium: BandReactivity;
|
|
610
|
+
/** high → swirl / twist amount. */
|
|
611
|
+
high: BandReactivity;
|
|
612
|
+
/** low → time evolution speed. */
|
|
613
|
+
low: BandReactivity;
|
|
614
|
+
cameraZ: number;
|
|
615
|
+
fov: number;
|
|
616
|
+
/**
|
|
617
|
+
* Seed used to generate the initial particle positions AND the wander
|
|
618
|
+
* phase. Same seed + same audio → same visual across playbacks.
|
|
619
|
+
*/
|
|
620
|
+
seed: number;
|
|
621
|
+
/** `null` picks `min(devicePixelRatio, 2)`. */
|
|
622
|
+
maxPixelRatio: number | null;
|
|
623
|
+
/** Scene clear color. `null` = transparent. */
|
|
624
|
+
backgroundColor: string | null;
|
|
625
|
+
}
|
|
626
|
+
/**
|
|
627
|
+
* Preset tuned to match the Shogo warm-orange palette used by
|
|
628
|
+
* `DEFAULT_ORGANIC_SPHERE_CONFIG`, so the two modes read as siblings.
|
|
629
|
+
* Count/size defaults target 60fps on mid-tier GPUs.
|
|
630
|
+
*/
|
|
631
|
+
declare const DEFAULT_ORGANIC_PARTICLES_CONFIG: OrganicParticlesConfig;
|
|
632
|
+
/**
|
|
633
|
+
* Merge a partial override into `DEFAULT_ORGANIC_PARTICLES_CONFIG`,
|
|
634
|
+
* with per-band shallow merges for `volume/medium/high/low`.
|
|
635
|
+
*/
|
|
636
|
+
declare function resolveOrganicParticlesConfig(partial?: Partial<OrganicParticlesConfig>): OrganicParticlesConfig;
|
|
637
|
+
|
|
638
|
+
/**
|
|
639
|
+
* GLSL shaders for the `OrganicSphere` audio-reactive visualization.
|
|
640
|
+
*
|
|
641
|
+
* Ported from Bruno Simon's open-source "organic-sphere" demo
|
|
642
|
+
* (https://github.com/brunosimon/organic-sphere — no license declared
|
|
643
|
+
* in the upstream repo at time of vendoring; treat as MIT-spirit
|
|
644
|
+
* reference code per the author's public body of work). The original
|
|
645
|
+
* uses a glslify `#pragma require` for the Perlin noise partial; we
|
|
646
|
+
* inline the partial here so the bundle is self-contained and the
|
|
647
|
+
* host app does not need a GLSL bundler plugin.
|
|
648
|
+
*
|
|
649
|
+
* The Perlin 3D/4D noise is Stefan Gustavson's classic implementation
|
|
650
|
+
* (public domain — see https://github.com/stegu/webgl-noise).
|
|
651
|
+
*/
|
|
652
|
+
/**
|
|
653
|
+
* Classic Perlin 4D noise (Stefan Gustavson). Inlined, used by every
|
|
654
|
+
* organic-* vertex shader in this module. Exported so sibling shader
|
|
655
|
+
* files (e.g. `organicParticlesShaders.ts`) can concatenate it without
|
|
656
|
+
* re-vendoring the implementation.
|
|
657
|
+
*/
|
|
658
|
+
declare const PERLIN_4D = "\nvec4 permute(vec4 x){return mod(((x*34.0)+1.0)*x, 289.0);}\nvec4 taylorInvSqrt(vec4 r){return 1.79284291400159 - 0.85373472095314 * r;}\nvec4 fade(vec4 t) {return t*t*t*(t*(t*6.0-15.0)+10.0);}\n\nfloat perlin4d(vec4 P){\n vec4 Pi0 = floor(P);\n vec4 Pi1 = Pi0 + 1.0;\n Pi0 = mod(Pi0, 289.0);\n Pi1 = mod(Pi1, 289.0);\n vec4 Pf0 = fract(P);\n vec4 Pf1 = Pf0 - 1.0;\n vec4 ix = vec4(Pi0.x, Pi1.x, Pi0.x, Pi1.x);\n vec4 iy = vec4(Pi0.yy, Pi1.yy);\n vec4 iz0 = vec4(Pi0.zzzz);\n vec4 iz1 = vec4(Pi1.zzzz);\n vec4 iw0 = vec4(Pi0.wwww);\n vec4 iw1 = vec4(Pi1.wwww);\n\n vec4 ixy = permute(permute(ix) + iy);\n vec4 ixy0 = permute(ixy + iz0);\n vec4 ixy1 = permute(ixy + iz1);\n vec4 ixy00 = permute(ixy0 + iw0);\n vec4 ixy01 = permute(ixy0 + iw1);\n vec4 ixy10 = permute(ixy1 + iw0);\n vec4 ixy11 = permute(ixy1 + iw1);\n\n vec4 gx00 = ixy00 / 7.0;\n vec4 gy00 = floor(gx00) / 7.0;\n vec4 gz00 = floor(gy00) / 6.0;\n gx00 = fract(gx00) - 0.5;\n gy00 = fract(gy00) - 0.5;\n gz00 = fract(gz00) - 0.5;\n vec4 gw00 = vec4(0.75) - abs(gx00) - abs(gy00) - abs(gz00);\n vec4 sw00 = step(gw00, vec4(0.0));\n gx00 -= sw00 * (step(0.0, gx00) - 0.5);\n gy00 -= sw00 * (step(0.0, gy00) - 0.5);\n\n vec4 gx01 = ixy01 / 7.0;\n vec4 gy01 = floor(gx01) / 7.0;\n vec4 gz01 = floor(gy01) / 6.0;\n gx01 = fract(gx01) - 0.5;\n gy01 = fract(gy01) - 0.5;\n gz01 = fract(gz01) - 0.5;\n vec4 gw01 = vec4(0.75) - abs(gx01) - abs(gy01) - abs(gz01);\n vec4 sw01 = step(gw01, vec4(0.0));\n gx01 -= sw01 * (step(0.0, gx01) - 0.5);\n gy01 -= sw01 * (step(0.0, gy01) - 0.5);\n\n vec4 gx10 = ixy10 / 7.0;\n vec4 gy10 = floor(gx10) / 7.0;\n vec4 gz10 = floor(gy10) / 6.0;\n gx10 = fract(gx10) - 0.5;\n gy10 = fract(gy10) - 0.5;\n gz10 = fract(gz10) - 0.5;\n vec4 gw10 = vec4(0.75) - abs(gx10) - abs(gy10) - abs(gz10);\n vec4 sw10 = step(gw10, vec4(0.0));\n gx10 -= sw10 * (step(0.0, gx10) - 0.5);\n gy10 -= sw10 * (step(0.0, gy10) - 0.5);\n\n vec4 gx11 = ixy11 / 7.0;\n vec4 gy11 = floor(gx11) / 7.0;\n vec4 gz11 = floor(gy11) / 6.0;\n gx11 = fract(gx11) - 0.5;\n gy11 = fract(gy11) - 0.5;\n gz11 = fract(gz11) - 0.5;\n vec4 gw11 = vec4(0.75) - abs(gx11) - abs(gy11) - abs(gz11);\n vec4 sw11 = step(gw11, vec4(0.0));\n gx11 -= sw11 * (step(0.0, gx11) - 0.5);\n gy11 -= sw11 * (step(0.0, gy11) - 0.5);\n\n vec4 g0000 = vec4(gx00.x,gy00.x,gz00.x,gw00.x);\n vec4 g1000 = vec4(gx00.y,gy00.y,gz00.y,gw00.y);\n vec4 g0100 = vec4(gx00.z,gy00.z,gz00.z,gw00.z);\n vec4 g1100 = vec4(gx00.w,gy00.w,gz00.w,gw00.w);\n vec4 g0010 = vec4(gx10.x,gy10.x,gz10.x,gw10.x);\n vec4 g1010 = vec4(gx10.y,gy10.y,gz10.y,gw10.y);\n vec4 g0110 = vec4(gx10.z,gy10.z,gz10.z,gw10.z);\n vec4 g1110 = vec4(gx10.w,gy10.w,gz10.w,gw10.w);\n vec4 g0001 = vec4(gx01.x,gy01.x,gz01.x,gw01.x);\n vec4 g1001 = vec4(gx01.y,gy01.y,gz01.y,gw01.y);\n vec4 g0101 = vec4(gx01.z,gy01.z,gz01.z,gw01.z);\n vec4 g1101 = vec4(gx01.w,gy01.w,gz01.w,gw01.w);\n vec4 g0011 = vec4(gx11.x,gy11.x,gz11.x,gw11.x);\n vec4 g1011 = vec4(gx11.y,gy11.y,gz11.y,gw11.y);\n vec4 g0111 = vec4(gx11.z,gy11.z,gz11.z,gw11.z);\n vec4 g1111 = vec4(gx11.w,gy11.w,gz11.w,gw11.w);\n\n vec4 norm00 = taylorInvSqrt(vec4(dot(g0000, g0000), dot(g0100, g0100), dot(g1000, g1000), dot(g1100, g1100)));\n g0000 *= norm00.x; g0100 *= norm00.y; g1000 *= norm00.z; g1100 *= norm00.w;\n\n vec4 norm01 = taylorInvSqrt(vec4(dot(g0001, g0001), dot(g0101, g0101), dot(g1001, g1001), dot(g1101, g1101)));\n g0001 *= norm01.x; g0101 *= norm01.y; g1001 *= norm01.z; g1101 *= norm01.w;\n\n vec4 norm10 = taylorInvSqrt(vec4(dot(g0010, g0010), dot(g0110, g0110), dot(g1010, g1010), dot(g1110, g1110)));\n g0010 *= norm10.x; g0110 *= norm10.y; g1010 *= norm10.z; g1110 *= norm10.w;\n\n vec4 norm11 = taylorInvSqrt(vec4(dot(g0011, g0011), dot(g0111, g0111), dot(g1011, g1011), dot(g1111, g1111)));\n g0011 *= norm11.x; g0111 *= norm11.y; g1011 *= norm11.z; g1111 *= norm11.w;\n\n float n0000 = dot(g0000, Pf0);\n float n1000 = dot(g1000, vec4(Pf1.x, Pf0.yzw));\n float n0100 = dot(g0100, vec4(Pf0.x, Pf1.y, Pf0.zw));\n float n1100 = dot(g1100, vec4(Pf1.xy, Pf0.zw));\n float n0010 = dot(g0010, vec4(Pf0.xy, Pf1.z, Pf0.w));\n float n1010 = dot(g1010, vec4(Pf1.x, Pf0.y, Pf1.z, Pf0.w));\n float n0110 = dot(g0110, vec4(Pf0.x, Pf1.yz, Pf0.w));\n float n1110 = dot(g1110, vec4(Pf1.xyz, Pf0.w));\n float n0001 = dot(g0001, vec4(Pf0.xyz, Pf1.w));\n float n1001 = dot(g1001, vec4(Pf1.x, Pf0.yz, Pf1.w));\n float n0101 = dot(g0101, vec4(Pf0.x, Pf1.y, Pf0.z, Pf1.w));\n float n1101 = dot(g1101, vec4(Pf1.xy, Pf0.z, Pf1.w));\n float n0011 = dot(g0011, vec4(Pf0.xy, Pf1.zw));\n float n1011 = dot(g1011, vec4(Pf1.x, Pf0.y, Pf1.zw));\n float n0111 = dot(g0111, vec4(Pf0.x, Pf1.yzw));\n float n1111 = dot(g1111, Pf1);\n\n vec4 fade_xyzw = fade(Pf0);\n vec4 n_0w = mix(vec4(n0000, n1000, n0100, n1100), vec4(n0001, n1001, n0101, n1101), fade_xyzw.w);\n vec4 n_1w = mix(vec4(n0010, n1010, n0110, n1110), vec4(n0011, n1011, n0111, n1111), fade_xyzw.w);\n vec4 n_zw = mix(n_0w, n_1w, fade_xyzw.z);\n vec2 n_yzw = mix(n_zw.xy, n_zw.zw, fade_xyzw.y);\n float n_xyzw = mix(n_yzw.x, n_yzw.y, fade_xyzw.x);\n return 2.2 * n_xyzw;\n}\n";
|
|
659
|
+
declare const ORGANIC_SPHERE_VERTEX_SHADER = "\n#define M_PI 3.1415926535897932384626433832795\n\nuniform vec3 uLightAColor;\nuniform vec3 uLightAPosition;\nuniform float uLightAIntensity;\nuniform vec3 uLightBColor;\nuniform vec3 uLightBPosition;\nuniform float uLightBIntensity;\n\nuniform vec2 uSubdivision;\n\nuniform vec3 uOffset;\n\nuniform float uDistortionFrequency;\nuniform float uDistortionStrength;\nuniform float uDisplacementFrequency;\nuniform float uDisplacementStrength;\n\nuniform float uFresnelOffset;\nuniform float uFresnelMultiplier;\nuniform float uFresnelPower;\n\nuniform vec3 uRimColor;\nuniform float uRimThreshold;\nuniform float uRimPower;\n\nuniform float uTime;\n\nvarying vec3 vColor;\n\n\nvec4 permute(vec4 x){return mod(((x*34.0)+1.0)*x, 289.0);}\nvec4 taylorInvSqrt(vec4 r){return 1.79284291400159 - 0.85373472095314 * r;}\nvec4 fade(vec4 t) {return t*t*t*(t*(t*6.0-15.0)+10.0);}\n\nfloat perlin4d(vec4 P){\n vec4 Pi0 = floor(P);\n vec4 Pi1 = Pi0 + 1.0;\n Pi0 = mod(Pi0, 289.0);\n Pi1 = mod(Pi1, 289.0);\n vec4 Pf0 = fract(P);\n vec4 Pf1 = Pf0 - 1.0;\n vec4 ix = vec4(Pi0.x, Pi1.x, Pi0.x, Pi1.x);\n vec4 iy = vec4(Pi0.yy, Pi1.yy);\n vec4 iz0 = vec4(Pi0.zzzz);\n vec4 iz1 = vec4(Pi1.zzzz);\n vec4 iw0 = vec4(Pi0.wwww);\n vec4 iw1 = vec4(Pi1.wwww);\n\n vec4 ixy = permute(permute(ix) + iy);\n vec4 ixy0 = permute(ixy + iz0);\n vec4 ixy1 = permute(ixy + iz1);\n vec4 ixy00 = permute(ixy0 + iw0);\n vec4 ixy01 = permute(ixy0 + iw1);\n vec4 ixy10 = permute(ixy1 + iw0);\n vec4 ixy11 = permute(ixy1 + iw1);\n\n vec4 gx00 = ixy00 / 7.0;\n vec4 gy00 = floor(gx00) / 7.0;\n vec4 gz00 = floor(gy00) / 6.0;\n gx00 = fract(gx00) - 0.5;\n gy00 = fract(gy00) - 0.5;\n gz00 = fract(gz00) - 0.5;\n vec4 gw00 = vec4(0.75) - abs(gx00) - abs(gy00) - abs(gz00);\n vec4 sw00 = step(gw00, vec4(0.0));\n gx00 -= sw00 * (step(0.0, gx00) - 0.5);\n gy00 -= sw00 * (step(0.0, gy00) - 0.5);\n\n vec4 gx01 = ixy01 / 7.0;\n vec4 gy01 = floor(gx01) / 7.0;\n vec4 gz01 = floor(gy01) / 6.0;\n gx01 = fract(gx01) - 0.5;\n gy01 = fract(gy01) - 0.5;\n gz01 = fract(gz01) - 0.5;\n vec4 gw01 = vec4(0.75) - abs(gx01) - abs(gy01) - abs(gz01);\n vec4 sw01 = step(gw01, vec4(0.0));\n gx01 -= sw01 * (step(0.0, gx01) - 0.5);\n gy01 -= sw01 * (step(0.0, gy01) - 0.5);\n\n vec4 gx10 = ixy10 / 7.0;\n vec4 gy10 = floor(gx10) / 7.0;\n vec4 gz10 = floor(gy10) / 6.0;\n gx10 = fract(gx10) - 0.5;\n gy10 = fract(gy10) - 0.5;\n gz10 = fract(gz10) - 0.5;\n vec4 gw10 = vec4(0.75) - abs(gx10) - abs(gy10) - abs(gz10);\n vec4 sw10 = step(gw10, vec4(0.0));\n gx10 -= sw10 * (step(0.0, gx10) - 0.5);\n gy10 -= sw10 * (step(0.0, gy10) - 0.5);\n\n vec4 gx11 = ixy11 / 7.0;\n vec4 gy11 = floor(gx11) / 7.0;\n vec4 gz11 = floor(gy11) / 6.0;\n gx11 = fract(gx11) - 0.5;\n gy11 = fract(gy11) - 0.5;\n gz11 = fract(gz11) - 0.5;\n vec4 gw11 = vec4(0.75) - abs(gx11) - abs(gy11) - abs(gz11);\n vec4 sw11 = step(gw11, vec4(0.0));\n gx11 -= sw11 * (step(0.0, gx11) - 0.5);\n gy11 -= sw11 * (step(0.0, gy11) - 0.5);\n\n vec4 g0000 = vec4(gx00.x,gy00.x,gz00.x,gw00.x);\n vec4 g1000 = vec4(gx00.y,gy00.y,gz00.y,gw00.y);\n vec4 g0100 = vec4(gx00.z,gy00.z,gz00.z,gw00.z);\n vec4 g1100 = vec4(gx00.w,gy00.w,gz00.w,gw00.w);\n vec4 g0010 = vec4(gx10.x,gy10.x,gz10.x,gw10.x);\n vec4 g1010 = vec4(gx10.y,gy10.y,gz10.y,gw10.y);\n vec4 g0110 = vec4(gx10.z,gy10.z,gz10.z,gw10.z);\n vec4 g1110 = vec4(gx10.w,gy10.w,gz10.w,gw10.w);\n vec4 g0001 = vec4(gx01.x,gy01.x,gz01.x,gw01.x);\n vec4 g1001 = vec4(gx01.y,gy01.y,gz01.y,gw01.y);\n vec4 g0101 = vec4(gx01.z,gy01.z,gz01.z,gw01.z);\n vec4 g1101 = vec4(gx01.w,gy01.w,gz01.w,gw01.w);\n vec4 g0011 = vec4(gx11.x,gy11.x,gz11.x,gw11.x);\n vec4 g1011 = vec4(gx11.y,gy11.y,gz11.y,gw11.y);\n vec4 g0111 = vec4(gx11.z,gy11.z,gz11.z,gw11.z);\n vec4 g1111 = vec4(gx11.w,gy11.w,gz11.w,gw11.w);\n\n vec4 norm00 = taylorInvSqrt(vec4(dot(g0000, g0000), dot(g0100, g0100), dot(g1000, g1000), dot(g1100, g1100)));\n g0000 *= norm00.x; g0100 *= norm00.y; g1000 *= norm00.z; g1100 *= norm00.w;\n\n vec4 norm01 = taylorInvSqrt(vec4(dot(g0001, g0001), dot(g0101, g0101), dot(g1001, g1001), dot(g1101, g1101)));\n g0001 *= norm01.x; g0101 *= norm01.y; g1001 *= norm01.z; g1101 *= norm01.w;\n\n vec4 norm10 = taylorInvSqrt(vec4(dot(g0010, g0010), dot(g0110, g0110), dot(g1010, g1010), dot(g1110, g1110)));\n g0010 *= norm10.x; g0110 *= norm10.y; g1010 *= norm10.z; g1110 *= norm10.w;\n\n vec4 norm11 = taylorInvSqrt(vec4(dot(g0011, g0011), dot(g0111, g0111), dot(g1011, g1011), dot(g1111, g1111)));\n g0011 *= norm11.x; g0111 *= norm11.y; g1011 *= norm11.z; g1111 *= norm11.w;\n\n float n0000 = dot(g0000, Pf0);\n float n1000 = dot(g1000, vec4(Pf1.x, Pf0.yzw));\n float n0100 = dot(g0100, vec4(Pf0.x, Pf1.y, Pf0.zw));\n float n1100 = dot(g1100, vec4(Pf1.xy, Pf0.zw));\n float n0010 = dot(g0010, vec4(Pf0.xy, Pf1.z, Pf0.w));\n float n1010 = dot(g1010, vec4(Pf1.x, Pf0.y, Pf1.z, Pf0.w));\n float n0110 = dot(g0110, vec4(Pf0.x, Pf1.yz, Pf0.w));\n float n1110 = dot(g1110, vec4(Pf1.xyz, Pf0.w));\n float n0001 = dot(g0001, vec4(Pf0.xyz, Pf1.w));\n float n1001 = dot(g1001, vec4(Pf1.x, Pf0.yz, Pf1.w));\n float n0101 = dot(g0101, vec4(Pf0.x, Pf1.y, Pf0.z, Pf1.w));\n float n1101 = dot(g1101, vec4(Pf1.xy, Pf0.z, Pf1.w));\n float n0011 = dot(g0011, vec4(Pf0.xy, Pf1.zw));\n float n1011 = dot(g1011, vec4(Pf1.x, Pf0.y, Pf1.zw));\n float n0111 = dot(g0111, vec4(Pf0.x, Pf1.yzw));\n float n1111 = dot(g1111, Pf1);\n\n vec4 fade_xyzw = fade(Pf0);\n vec4 n_0w = mix(vec4(n0000, n1000, n0100, n1100), vec4(n0001, n1001, n0101, n1101), fade_xyzw.w);\n vec4 n_1w = mix(vec4(n0010, n1010, n0110, n1110), vec4(n0011, n1011, n0111, n1111), fade_xyzw.w);\n vec4 n_zw = mix(n_0w, n_1w, fade_xyzw.z);\n vec2 n_yzw = mix(n_zw.xy, n_zw.zw, fade_xyzw.y);\n float n_xyzw = mix(n_yzw.x, n_yzw.y, fade_xyzw.x);\n return 2.2 * n_xyzw;\n}\n\n\nvec3 getDisplacedPosition(vec3 _position)\n{\n vec3 distoredPosition = _position;\n distoredPosition += perlin4d(vec4(distoredPosition * uDistortionFrequency + uOffset, uTime)) * uDistortionStrength;\n\n float perlinStrength = perlin4d(vec4(distoredPosition * uDisplacementFrequency + uOffset, uTime));\n\n vec3 displacedPosition = _position;\n displacedPosition += normalize(_position) * perlinStrength * uDisplacementStrength;\n\n return displacedPosition;\n}\n\nvoid main()\n{\n vec3 displacedPosition = getDisplacedPosition(position);\n vec4 viewPosition = viewMatrix * vec4(displacedPosition, 1.0);\n gl_Position = projectionMatrix * viewPosition;\n\n float distanceA = (M_PI * 2.0) / uSubdivision.x;\n float distanceB = M_PI / uSubdivision.x;\n\n vec3 biTangent = cross(normal, tangent.xyz);\n\n vec3 positionA = position + tangent.xyz * distanceA;\n vec3 displacedPositionA = getDisplacedPosition(positionA);\n\n vec3 positionB = position + biTangent.xyz * distanceB;\n vec3 displacedPositionB = getDisplacedPosition(positionB);\n\n vec3 computedNormal = cross(displacedPositionA - displacedPosition.xyz, displacedPositionB - displacedPosition.xyz);\n computedNormal = normalize(computedNormal);\n\n vec3 viewDirection = normalize(displacedPosition.xyz - cameraPosition);\n float fresnel = uFresnelOffset + (1.0 + dot(viewDirection, computedNormal)) * uFresnelMultiplier;\n fresnel = pow(max(0.0, fresnel), uFresnelPower);\n\n float lightAIntensity = max(0.0, - dot(computedNormal.xyz, normalize(- uLightAPosition))) * uLightAIntensity;\n float lightBIntensity = max(0.0, - dot(computedNormal.xyz, normalize(- uLightBPosition))) * uLightBIntensity;\n\n // Clamp mix factors to [0, 1] so a high lightIntensity*fresnel (common on\n // displacement peaks and loud audio) cannot extrapolate past the orange\n // light color and saturate R, producing yellow-white hotspots.\n vec3 color = vec3(0.0);\n color = mix(color, uLightAColor, clamp(lightAIntensity * fresnel, 0.0, 1.0));\n color = mix(color, uLightBColor, clamp(lightBIntensity * fresnel, 0.0, 1.0));\n color = mix(color, uRimColor, clamp(pow(max(0.0, fresnel - uRimThreshold), uRimPower), 0.0, 1.0));\n\n vColor = color;\n}\n";
|
|
660
|
+
declare const ORGANIC_SPHERE_FRAGMENT_SHADER = "\nvarying vec3 vColor;\n\nvoid main()\n{\n gl_FragColor = vec4(vColor, 1.0);\n}\n";
|
|
661
|
+
|
|
662
|
+
/**
|
|
663
|
+
* GLSL shaders for the `OrganicParticles` audio-reactive visualization —
|
|
664
|
+
* a sibling of `OrganicSphere` that replaces the solid mesh with a
|
|
665
|
+
* spherical cloud of point-sprite particles whose radial displacement,
|
|
666
|
+
* swirl, size, and color gradient are driven by the same four frequency
|
|
667
|
+
* bands.
|
|
668
|
+
*
|
|
669
|
+
* The Perlin 4D noise is shared with the sphere shader via the re-usable
|
|
670
|
+
* `PERLIN_4D` partial so we only vendor Stefan Gustavson's implementation
|
|
671
|
+
* once.
|
|
672
|
+
*/
|
|
673
|
+
/**
|
|
674
|
+
* Vertex shader. Reads a static `aBasePosition` (on a deterministic
|
|
675
|
+
* seeded sphere) plus a per-particle `aSeed` (0–1) for subtle variation,
|
|
676
|
+
* then:
|
|
677
|
+
* 1. Displaces each particle radially by Perlin noise + audio volume.
|
|
678
|
+
* 2. Twists the cloud around the Y axis by an amount driven by audio
|
|
679
|
+
* high-band (`uSwirl`).
|
|
680
|
+
* 3. Writes `vHeat` in [0, 1] for the fragment to mix between the two
|
|
681
|
+
* tint colors.
|
|
682
|
+
* 4. Sizes the point sprite by distance (perspective) + audio medium.
|
|
683
|
+
*/
|
|
684
|
+
declare const ORGANIC_PARTICLES_VERTEX_SHADER = "\n\nvec4 permute(vec4 x){return mod(((x*34.0)+1.0)*x, 289.0);}\nvec4 taylorInvSqrt(vec4 r){return 1.79284291400159 - 0.85373472095314 * r;}\nvec4 fade(vec4 t) {return t*t*t*(t*(t*6.0-15.0)+10.0);}\n\nfloat perlin4d(vec4 P){\n vec4 Pi0 = floor(P);\n vec4 Pi1 = Pi0 + 1.0;\n Pi0 = mod(Pi0, 289.0);\n Pi1 = mod(Pi1, 289.0);\n vec4 Pf0 = fract(P);\n vec4 Pf1 = Pf0 - 1.0;\n vec4 ix = vec4(Pi0.x, Pi1.x, Pi0.x, Pi1.x);\n vec4 iy = vec4(Pi0.yy, Pi1.yy);\n vec4 iz0 = vec4(Pi0.zzzz);\n vec4 iz1 = vec4(Pi1.zzzz);\n vec4 iw0 = vec4(Pi0.wwww);\n vec4 iw1 = vec4(Pi1.wwww);\n\n vec4 ixy = permute(permute(ix) + iy);\n vec4 ixy0 = permute(ixy + iz0);\n vec4 ixy1 = permute(ixy + iz1);\n vec4 ixy00 = permute(ixy0 + iw0);\n vec4 ixy01 = permute(ixy0 + iw1);\n vec4 ixy10 = permute(ixy1 + iw0);\n vec4 ixy11 = permute(ixy1 + iw1);\n\n vec4 gx00 = ixy00 / 7.0;\n vec4 gy00 = floor(gx00) / 7.0;\n vec4 gz00 = floor(gy00) / 6.0;\n gx00 = fract(gx00) - 0.5;\n gy00 = fract(gy00) - 0.5;\n gz00 = fract(gz00) - 0.5;\n vec4 gw00 = vec4(0.75) - abs(gx00) - abs(gy00) - abs(gz00);\n vec4 sw00 = step(gw00, vec4(0.0));\n gx00 -= sw00 * (step(0.0, gx00) - 0.5);\n gy00 -= sw00 * (step(0.0, gy00) - 0.5);\n\n vec4 gx01 = ixy01 / 7.0;\n vec4 gy01 = floor(gx01) / 7.0;\n vec4 gz01 = floor(gy01) / 6.0;\n gx01 = fract(gx01) - 0.5;\n gy01 = fract(gy01) - 0.5;\n gz01 = fract(gz01) - 0.5;\n vec4 gw01 = vec4(0.75) - abs(gx01) - abs(gy01) - abs(gz01);\n vec4 sw01 = step(gw01, vec4(0.0));\n gx01 -= sw01 * (step(0.0, gx01) - 0.5);\n gy01 -= sw01 * (step(0.0, gy01) - 0.5);\n\n vec4 gx10 = ixy10 / 7.0;\n vec4 gy10 = floor(gx10) / 7.0;\n vec4 gz10 = floor(gy10) / 6.0;\n gx10 = fract(gx10) - 0.5;\n gy10 = fract(gy10) - 0.5;\n gz10 = fract(gz10) - 0.5;\n vec4 gw10 = vec4(0.75) - abs(gx10) - abs(gy10) - abs(gz10);\n vec4 sw10 = step(gw10, vec4(0.0));\n gx10 -= sw10 * (step(0.0, gx10) - 0.5);\n gy10 -= sw10 * (step(0.0, gy10) - 0.5);\n\n vec4 gx11 = ixy11 / 7.0;\n vec4 gy11 = floor(gx11) / 7.0;\n vec4 gz11 = floor(gy11) / 6.0;\n gx11 = fract(gx11) - 0.5;\n gy11 = fract(gy11) - 0.5;\n gz11 = fract(gz11) - 0.5;\n vec4 gw11 = vec4(0.75) - abs(gx11) - abs(gy11) - abs(gz11);\n vec4 sw11 = step(gw11, vec4(0.0));\n gx11 -= sw11 * (step(0.0, gx11) - 0.5);\n gy11 -= sw11 * (step(0.0, gy11) - 0.5);\n\n vec4 g0000 = vec4(gx00.x,gy00.x,gz00.x,gw00.x);\n vec4 g1000 = vec4(gx00.y,gy00.y,gz00.y,gw00.y);\n vec4 g0100 = vec4(gx00.z,gy00.z,gz00.z,gw00.z);\n vec4 g1100 = vec4(gx00.w,gy00.w,gz00.w,gw00.w);\n vec4 g0010 = vec4(gx10.x,gy10.x,gz10.x,gw10.x);\n vec4 g1010 = vec4(gx10.y,gy10.y,gz10.y,gw10.y);\n vec4 g0110 = vec4(gx10.z,gy10.z,gz10.z,gw10.z);\n vec4 g1110 = vec4(gx10.w,gy10.w,gz10.w,gw10.w);\n vec4 g0001 = vec4(gx01.x,gy01.x,gz01.x,gw01.x);\n vec4 g1001 = vec4(gx01.y,gy01.y,gz01.y,gw01.y);\n vec4 g0101 = vec4(gx01.z,gy01.z,gz01.z,gw01.z);\n vec4 g1101 = vec4(gx01.w,gy01.w,gz01.w,gw01.w);\n vec4 g0011 = vec4(gx11.x,gy11.x,gz11.x,gw11.x);\n vec4 g1011 = vec4(gx11.y,gy11.y,gz11.y,gw11.y);\n vec4 g0111 = vec4(gx11.z,gy11.z,gz11.z,gw11.z);\n vec4 g1111 = vec4(gx11.w,gy11.w,gz11.w,gw11.w);\n\n vec4 norm00 = taylorInvSqrt(vec4(dot(g0000, g0000), dot(g0100, g0100), dot(g1000, g1000), dot(g1100, g1100)));\n g0000 *= norm00.x; g0100 *= norm00.y; g1000 *= norm00.z; g1100 *= norm00.w;\n\n vec4 norm01 = taylorInvSqrt(vec4(dot(g0001, g0001), dot(g0101, g0101), dot(g1001, g1001), dot(g1101, g1101)));\n g0001 *= norm01.x; g0101 *= norm01.y; g1001 *= norm01.z; g1101 *= norm01.w;\n\n vec4 norm10 = taylorInvSqrt(vec4(dot(g0010, g0010), dot(g0110, g0110), dot(g1010, g1010), dot(g1110, g1110)));\n g0010 *= norm10.x; g0110 *= norm10.y; g1010 *= norm10.z; g1110 *= norm10.w;\n\n vec4 norm11 = taylorInvSqrt(vec4(dot(g0011, g0011), dot(g0111, g0111), dot(g1011, g1011), dot(g1111, g1111)));\n g0011 *= norm11.x; g0111 *= norm11.y; g1011 *= norm11.z; g1111 *= norm11.w;\n\n float n0000 = dot(g0000, Pf0);\n float n1000 = dot(g1000, vec4(Pf1.x, Pf0.yzw));\n float n0100 = dot(g0100, vec4(Pf0.x, Pf1.y, Pf0.zw));\n float n1100 = dot(g1100, vec4(Pf1.xy, Pf0.zw));\n float n0010 = dot(g0010, vec4(Pf0.xy, Pf1.z, Pf0.w));\n float n1010 = dot(g1010, vec4(Pf1.x, Pf0.y, Pf1.z, Pf0.w));\n float n0110 = dot(g0110, vec4(Pf0.x, Pf1.yz, Pf0.w));\n float n1110 = dot(g1110, vec4(Pf1.xyz, Pf0.w));\n float n0001 = dot(g0001, vec4(Pf0.xyz, Pf1.w));\n float n1001 = dot(g1001, vec4(Pf1.x, Pf0.yz, Pf1.w));\n float n0101 = dot(g0101, vec4(Pf0.x, Pf1.y, Pf0.z, Pf1.w));\n float n1101 = dot(g1101, vec4(Pf1.xy, Pf0.z, Pf1.w));\n float n0011 = dot(g0011, vec4(Pf0.xy, Pf1.zw));\n float n1011 = dot(g1011, vec4(Pf1.x, Pf0.y, Pf1.zw));\n float n0111 = dot(g0111, vec4(Pf0.x, Pf1.yzw));\n float n1111 = dot(g1111, Pf1);\n\n vec4 fade_xyzw = fade(Pf0);\n vec4 n_0w = mix(vec4(n0000, n1000, n0100, n1100), vec4(n0001, n1001, n0101, n1101), fade_xyzw.w);\n vec4 n_1w = mix(vec4(n0010, n1010, n0110, n1110), vec4(n0011, n1011, n0111, n1111), fade_xyzw.w);\n vec4 n_zw = mix(n_0w, n_1w, fade_xyzw.z);\n vec2 n_yzw = mix(n_zw.xy, n_zw.zw, fade_xyzw.y);\n float n_xyzw = mix(n_yzw.x, n_yzw.y, fade_xyzw.x);\n return 2.2 * n_xyzw;\n}\n\n\nattribute vec3 aBasePosition;\nattribute float aSeed;\n\nuniform float uTime;\nuniform float uNoiseFrequency;\nuniform float uNoiseStrength;\nuniform float uDisplacement;\nuniform float uSwirl;\nuniform float uSwirlTimeRate;\nuniform float uSizeBase;\nuniform float uSizeAudio;\nuniform float uPixelRatio;\n\nvarying float vHeat;\nvarying float vSeed;\n\nvoid main() {\n vec3 pos = aBasePosition;\n\n // 4D Perlin sampled in particle space. The 4th axis is time so the\n // cloud evolves over time without tiling.\n float n = perlin4d(vec4(pos * uNoiseFrequency, uTime));\n\n // Per-particle phase so the cloud doesn't breathe in lockstep.\n float phaseShift = (aSeed - 0.5) * 0.5;\n float breath = sin(uTime * 0.0008 + aSeed * 6.2831) * 0.5 + 0.5;\n\n // Outward push: noise + audio-driven displacement, scaled by seed so\n // some particles dart further than others on the same beat.\n vec3 radial = normalize(pos);\n float push = n * uNoiseStrength + uDisplacement * (0.6 + aSeed * 0.8) + breath * 0.05;\n pos += radial * push;\n\n // Twist around Y. Amount scales with height so the cloud spirals\n // rather than rigid-rotating. uSwirlTimeRate is 0 by default so the\n // swirl is driven by audio (uSwirl) alone; crank it for a constant\n // drift.\n float swirlAngle = pos.y * uSwirl + uTime * uSwirlTimeRate + phaseShift;\n float c = cos(swirlAngle);\n float s = sin(swirlAngle);\n pos.xz = mat2(c, -s, s, c) * pos.xz;\n\n vec4 mvPos = modelViewMatrix * vec4(pos, 1.0);\n gl_Position = projectionMatrix * mvPos;\n\n // Point size: base + audio boost, gently perspective-attenuated. At\n // the default camera (z ~= 3.84) a uSizeBase of 1 renders roughly one\n // device pixel \u2014 intentionally tiny so 40k+ additively-blended points\n // read as fine dust rather than a solid blob.\n float size = uSizeBase + uSizeAudio * (0.5 + aSeed);\n gl_PointSize = max(1.0, size * uPixelRatio * (2.0 / max(0.1, -mvPos.z)));\n\n // Heat: 0 at core, 1 at fringe. Used by the fragment to lerp color.\n vHeat = clamp((length(pos) - 0.4) / 1.4, 0.0, 1.0);\n vSeed = aSeed;\n}\n";
|
|
685
|
+
/**
|
|
686
|
+
* Fragment shader. Draws each particle as a soft disc that mixes between
|
|
687
|
+
* the cool and hot tint based on `vHeat`, with per-particle noise-free
|
|
688
|
+
* alpha shaping for a plasma-dust feel. Paired with additive blending
|
|
689
|
+
* and no depth-write for a glowing cloud that doesn't self-occlude.
|
|
690
|
+
*/
|
|
691
|
+
declare const ORGANIC_PARTICLES_FRAGMENT_SHADER = "\nprecision mediump float;\n\nuniform vec3 uColorCool;\nuniform vec3 uColorHot;\nuniform float uOpacity;\nuniform float uSoftness;\n\nvarying float vHeat;\nvarying float vSeed;\n\nvoid main() {\n vec2 centered = gl_PointCoord - vec2(0.5);\n float distSq = dot(centered, centered);\n if (distSq > 0.25) discard;\n\n // Soft circular falloff. uSoftness in [0.5, 1] controls how hard the\n // edge is: 0.5 = sharp disc, 1 = feathered blob.\n float innerR = 0.25 * (1.0 - uSoftness);\n float alpha = smoothstep(0.25, innerR, distSq);\n\n // Warm/cool gradient, with a tiny seed-based tint wobble so the cloud\n // doesn't look banded.\n float wobble = (vSeed - 0.5) * 0.15;\n vec3 color = mix(uColorCool, uColorHot, clamp(vHeat + wobble, 0.0, 1.0));\n\n gl_FragColor = vec4(color, alpha * uOpacity);\n}\n";
|
|
692
|
+
|
|
693
|
+
export { type BaseVoiceConversationOptions as B, type ChatClientToolFn as C, DEFAULT_ORGANIC_PARTICLES_CONFIG as D, type OrganicSphereConfig as O, PERLIN_4D as P, type BaseVoiceConversationResult as a, type BaseChatConversationOptions as b, type BaseChatConversationResult as c, type OrganicParticlesConfig as d, type BandReactivity as e, type ChatConversationStatus as f, type ChatToolDescriptor as g, DEFAULT_ORGANIC_SPHERE_CONFIG as h, ORGANIC_PARTICLES_FRAGMENT_SHADER as i, ORGANIC_PARTICLES_VERTEX_SHADER as j, ORGANIC_SPHERE_FRAGMENT_SHADER as k, ORGANIC_SPHERE_VERTEX_SHADER as l, resolveOrganicSphereConfig as m, resolveOrganicParticlesConfig as r };
|