@spatialwalk/avatarkit 1.0.0-beta.61 → 1.0.0-beta.62

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/CHANGELOG.md CHANGED
@@ -2,6 +2,19 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file.
4
4
 
5
+ ## [1.0.0-beta.62] - 2026-01-14
6
+
7
+ ### ✨ New Features
8
+ - **Bezier Curve Transition Animation** - Implemented Bezier curve easing functions for smoother transitions
9
+ - Added Bezier curve interpolation with different curves for different facial components (jaw, expression, eye, neck, global)
10
+ - Replaced linear interpolation with Bezier curve interpolation for more natural animation
11
+ - Split transition duration into start (200ms) and end (1600ms) for different transition types
12
+
13
+ ### 🔧 Improvements
14
+ - **Transition API Enhancement** - Updated `generateTransitionFromIdle()` to support both start and end transitions
15
+ - Added `transitionType` parameter: `'start'` for Idle -> Flame, `'end'` for Flame -> Idle
16
+ - Removed deprecated linear interpolation code and unused easing functions
17
+
5
18
  ## [1.0.0-beta.61] - 2026-01-14
6
19
 
7
20
  ### 🔧 Improvements
@@ -1,7 +1,7 @@
1
1
  var __defProp = Object.defineProperty;
2
2
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
3
3
  var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
4
- import { A as APP_CONFIG, l as logger, e as errorToMessage, a as logEvent } from "./index-jWgogoMs.js";
4
+ import { A as APP_CONFIG, l as logger, e as errorToMessage, a as logEvent } from "./index-Bhjn1nq3.js";
5
5
  class StreamingAudioPlayer {
6
6
  constructor(options) {
7
7
  __publicField(this, "audioContext", null);
@@ -22,7 +22,8 @@ export declare class AvatarView {
22
22
  private currentFPS;
23
23
  private transitionKeyframes;
24
24
  private transitionStartTime;
25
- private readonly transitionDurationMs;
25
+ private readonly startTransitionDurationMs;
26
+ private readonly endTransitionDurationMs;
26
27
  private cachedIdleFirstFrame;
27
28
  private idleCurrentFrameIndex;
28
29
  private characterHandle;
@@ -61,7 +62,7 @@ export declare class AvatarView {
61
62
  private stopRealtimeRendering;
62
63
  dispose(): void;
63
64
  renderFlame(flame: Flame, enableIdleRendering?: boolean): Promise<void>;
64
- generateTransitionFromIdle(toFlame: Flame, frameCount: number): Promise<Flame[]>;
65
+ generateTransitionFromIdle(toFlame: Flame, frameCount: number, transitionType?: 'start' | 'end'): Promise<Flame[]>;
65
66
  private rerenderCurrentFrameWithNewCamera;
66
67
  private handleResize;
67
68
  get transform(): {
@@ -7624,7 +7624,7 @@ const _AnimationPlayer = class _AnimationPlayer {
7624
7624
  if (this.streamingPlayer) {
7625
7625
  return;
7626
7626
  }
7627
- const { StreamingAudioPlayer } = await import("./StreamingAudioPlayer-DIcPerS7.js");
7627
+ const { StreamingAudioPlayer } = await import("./StreamingAudioPlayer-BWsAt_s7.js");
7628
7628
  const { AvatarSDK: AvatarSDK2 } = await Promise.resolve().then(() => AvatarSDK$1);
7629
7629
  const audioFormat = AvatarSDK2.getAudioFormat();
7630
7630
  this.streamingPlayer = new StreamingAudioPlayer({
@@ -8961,7 +8961,7 @@ class AvatarSDK {
8961
8961
  }
8962
8962
  __publicField(AvatarSDK, "_isInitialized", false);
8963
8963
  __publicField(AvatarSDK, "_configuration", null);
8964
- __publicField(AvatarSDK, "_version", "1.0.0-beta.61");
8964
+ __publicField(AvatarSDK, "_version", "1.0.0-beta.62");
8965
8965
  __publicField(AvatarSDK, "_avatarCore", null);
8966
8966
  __publicField(AvatarSDK, "_dynamicSdkConfig", null);
8967
8967
  const AvatarSDK$1 = Object.freeze(Object.defineProperty({
@@ -13851,31 +13851,82 @@ function lerpArrays(from, to2, progress) {
13851
13851
  }
13852
13852
  return result2;
13853
13853
  }
13854
- function linearLerp(from, to2, progress) {
13855
- const easedProgress = 0.5 - Math.cos(progress * Math.PI) * 0.5;
13854
+ const clamp01 = (x2) => Math.max(0, Math.min(1, x2));
13855
+ function createBezierEasing(x1, y1, x2, y2) {
13856
+ const cx = 3 * x1;
13857
+ const bx = 3 * (x2 - x1) - cx;
13858
+ const ax = 1 - cx - bx;
13859
+ const cy = 3 * y1;
13860
+ const by = 3 * (y2 - y1) - cy;
13861
+ const ay = 1 - cy - by;
13862
+ const sampleCurveX = (t2) => ((ax * t2 + bx) * t2 + cx) * t2;
13863
+ const sampleCurveY = (t2) => ((ay * t2 + by) * t2 + cy) * t2;
13864
+ const sampleCurveDerivativeX = (t2) => (3 * ax * t2 + 2 * bx) * t2 + cx;
13865
+ const solveCurveX = (x3) => {
13866
+ let t2 = x3;
13867
+ for (let i2 = 0; i2 < 8; i2++) {
13868
+ const error = sampleCurveX(t2) - x3;
13869
+ if (Math.abs(error) < 1e-6) break;
13870
+ const d2 = sampleCurveDerivativeX(t2);
13871
+ if (Math.abs(d2) < 1e-6) break;
13872
+ t2 -= error / d2;
13873
+ }
13874
+ return t2;
13875
+ };
13876
+ return (x3) => {
13877
+ if (x3 <= 0) return 0;
13878
+ if (x3 >= 1) return 1;
13879
+ return sampleCurveY(solveCurveX(x3));
13880
+ };
13881
+ }
13882
+ const BEZIER_CURVES = {
13883
+ jaw: createBezierEasing(0.2, 0.8, 0.3, 1),
13884
+ expression: createBezierEasing(0.4, 0, 0.2, 1),
13885
+ eye: createBezierEasing(0.3, 0, 0.1, 1),
13886
+ neck: createBezierEasing(0.1, 0.2, 0.2, 1),
13887
+ global: createBezierEasing(0.42, 0, 0.58, 1)
13888
+ };
13889
+ const TIME_SCALE = {
13890
+ jaw: 2.5,
13891
+ expression: 1.6,
13892
+ eye: 1.3,
13893
+ neck: 1,
13894
+ global: 1
13895
+ };
13896
+ function bezierLerp(from, to2, progress) {
13897
+ const getT = (key) => {
13898
+ const scaledProgress = clamp01(progress * TIME_SCALE[key]);
13899
+ return BEZIER_CURVES[key](scaledProgress);
13900
+ };
13856
13901
  return {
13857
- translation: lerpArrays(from.translation || [0, 0, 0], to2.translation || [0, 0, 0], easedProgress),
13858
- rotation: lerpArrays(from.rotation || [0, 0, 0], to2.rotation || [0, 0, 0], easedProgress),
13859
- neckPose: lerpArrays(from.neckPose || [0, 0, 0], to2.neckPose || [0, 0, 0], easedProgress),
13860
- jawPose: lerpArrays(from.jawPose || [0, 0, 0], to2.jawPose || [0, 0, 0], easedProgress),
13861
- eyePose: lerpArrays(from.eyePose || [0, 0, 0, 0, 0, 0], to2.eyePose || [0, 0, 0, 0, 0, 0], easedProgress),
13902
+ translation: lerpArrays(from.translation || [0, 0, 0], to2.translation || [0, 0, 0], getT("global")),
13903
+ rotation: lerpArrays(from.rotation || [0, 0, 0], to2.rotation || [0, 0, 0], getT("global")),
13904
+ neckPose: lerpArrays(from.neckPose || [0, 0, 0], to2.neckPose || [0, 0, 0], getT("neck")),
13905
+ jawPose: lerpArrays(from.jawPose || [0, 0, 0], to2.jawPose || [0, 0, 0], getT("jaw")),
13906
+ eyePose: lerpArrays(from.eyePose || [0, 0, 0, 0, 0, 0], to2.eyePose || [0, 0, 0, 0, 0, 0], getT("eye")),
13862
13907
  eyeLid: (() => {
13863
13908
  const fromEyelid = from.eyeLid;
13864
13909
  const toEyelid = to2.eyeLid;
13865
- if (fromEyelid && fromEyelid.length > 0 && toEyelid && toEyelid.length > 0)
13866
- return lerpArrays(fromEyelid, toEyelid, easedProgress);
13910
+ if ((fromEyelid == null ? void 0 : fromEyelid.length) && (toEyelid == null ? void 0 : toEyelid.length))
13911
+ return lerpArrays(fromEyelid, toEyelid, getT("eye"));
13867
13912
  return fromEyelid || toEyelid || [];
13868
13913
  })(),
13869
- expression: lerpArrays(from.expression || [], to2.expression || [], easedProgress)
13914
+ expression: lerpArrays(from.expression || [], to2.expression || [], getT("expression"))
13870
13915
  };
13871
13916
  }
13872
13917
  function generateTransitionFrames(from, to2, durationMs, fps = 25) {
13873
13918
  const steps = Math.max(1, Math.floor(durationMs / 1e3 * fps));
13874
13919
  const frames = Array.from({ length: steps });
13920
+ if (steps === 1) {
13921
+ frames[0] = to2;
13922
+ return frames;
13923
+ }
13875
13924
  for (let i2 = 0; i2 < steps; i2++) {
13876
13925
  const progress = i2 / (steps - 1);
13877
- frames[i2] = linearLerp(from, to2, progress);
13926
+ frames[i2] = bezierLerp(from, to2, progress);
13878
13927
  }
13928
+ frames[0] = from;
13929
+ frames[frames.length - 1] = to2;
13879
13930
  return frames;
13880
13931
  }
13881
13932
  class AvatarView {
@@ -13900,7 +13951,8 @@ class AvatarView {
13900
13951
  __publicField(this, "currentFPS", 0);
13901
13952
  __publicField(this, "transitionKeyframes", []);
13902
13953
  __publicField(this, "transitionStartTime", 0);
13903
- __publicField(this, "transitionDurationMs", 400);
13954
+ __publicField(this, "startTransitionDurationMs", 200);
13955
+ __publicField(this, "endTransitionDurationMs", 1600);
13904
13956
  __publicField(this, "cachedIdleFirstFrame", null);
13905
13957
  __publicField(this, "idleCurrentFrameIndex", 0);
13906
13958
  __publicField(this, "characterHandle", null);
@@ -13957,12 +14009,12 @@ class AvatarView {
13957
14009
  toFixed.expression = ensureLen(toFixed.expression, exprLen);
13958
14010
  return { from: fromFixed, to: toFixed };
13959
14011
  }
13960
- generateAndAlignTransitionFrames(from, to2) {
14012
+ generateAndAlignTransitionFrames(from, to2, durationMs) {
13961
14013
  const aligned = this.alignFlamePair(from, to2);
13962
14014
  let keyframes = generateTransitionFrames(
13963
14015
  aligned.from,
13964
14016
  aligned.to,
13965
- this.transitionDurationMs,
14017
+ durationMs,
13966
14018
  APP_CONFIG.animation.fps
13967
14019
  );
13968
14020
  if (keyframes.length < 2) {
@@ -14321,7 +14373,8 @@ class AvatarView {
14321
14373
  return;
14322
14374
  }
14323
14375
  const elapsed = performance.now() - this.transitionStartTime;
14324
- const progress = Math.min(1, Math.max(0, elapsed / this.transitionDurationMs));
14376
+ const currentTransitionDurationMs = state === "transitioningToSpeaking" ? this.startTransitionDurationMs : this.endTransitionDurationMs;
14377
+ const progress = Math.min(1, Math.max(0, elapsed / currentTransitionDurationMs));
14325
14378
  const steps = this.transitionKeyframes.length;
14326
14379
  const idx = Math.min(steps - 1, Math.floor(progress * (steps - 1)));
14327
14380
  const currentFrame = this.transitionKeyframes[idx];
@@ -14347,7 +14400,7 @@ class AvatarView {
14347
14400
  return;
14348
14401
  }
14349
14402
  }
14350
- if (state === "transitioningToSpeaking" && this.transitionStartTime > 0 && this.transitionKeyframes.length > 0 && elapsed >= this.transitionDurationMs + 100) {
14403
+ if (state === "transitioningToSpeaking" && this.transitionStartTime > 0 && this.transitionKeyframes.length > 0 && elapsed >= this.startTransitionDurationMs + 100) {
14351
14404
  this.setState("speaking");
14352
14405
  this.transitionKeyframes = [];
14353
14406
  this.avatarController.onTransitionComplete();
@@ -14477,7 +14530,7 @@ class AvatarView {
14477
14530
  await this.getCachedIdleFirstFrame();
14478
14531
  const firstSpeaking = keyframes[0];
14479
14532
  const firstSpeakingWithPostProcessing = this.avatarController.applyPostProcessingToFlame(firstSpeaking);
14480
- this.transitionKeyframes = this.generateAndAlignTransitionFrames(idleFrameProto, firstSpeakingWithPostProcessing);
14533
+ this.transitionKeyframes = this.generateAndAlignTransitionFrames(idleFrameProto, firstSpeakingWithPostProcessing, this.startTransitionDurationMs);
14481
14534
  this.transitionStartTime = performance.now();
14482
14535
  if (this.transitionKeyframes.length === 0) {
14483
14536
  this.setState("speaking");
@@ -14530,7 +14583,7 @@ class AvatarView {
14530
14583
  const lastSpeaking = this.avatarController.applyPostProcessingToFlame(lastSpeakingRaw);
14531
14584
  const idleFirstProto = await this.getCachedIdleFirstFrame();
14532
14585
  if (idleFirstProto) {
14533
- this.transitionKeyframes = this.generateAndAlignTransitionFrames(lastSpeaking, idleFirstProto);
14586
+ this.transitionKeyframes = this.generateAndAlignTransitionFrames(lastSpeaking, idleFirstProto, this.endTransitionDurationMs);
14534
14587
  this.transitionStartTime = performance.now();
14535
14588
  if (this.transitionKeyframes.length > 0 && this.renderingState === "transitioningToIdle") {
14536
14589
  if (APP_CONFIG.debug)
@@ -14640,7 +14693,7 @@ class AvatarView {
14640
14693
  throw error;
14641
14694
  }
14642
14695
  }
14643
- async generateTransitionFromIdle(toFlame, frameCount) {
14696
+ async generateTransitionFromIdle(toFlame, frameCount, transitionType = "start") {
14644
14697
  if (!this.isInitialized) {
14645
14698
  throw new Error("AvatarView not initialized");
14646
14699
  }
@@ -14656,16 +14709,18 @@ class AvatarView {
14656
14709
  const idleFrameProto = convertWasmParamsToProtoFlame(idleParams);
14657
14710
  const toFlameWithPostProcessing = this.avatarController.applyPostProcessingToFlame(toFlame);
14658
14711
  const aligned = this.alignFlamePair(idleFrameProto, toFlameWithPostProcessing);
14712
+ const from = transitionType === "start" ? aligned.from : aligned.to;
14713
+ const to2 = transitionType === "start" ? aligned.to : aligned.from;
14659
14714
  const fps = APP_CONFIG.animation.fps;
14660
14715
  const durationMs = frameCount / fps * 1e3;
14661
14716
  const transitionFrames = generateTransitionFrames(
14662
- aligned.from,
14663
- aligned.to,
14717
+ from,
14718
+ to2,
14664
14719
  durationMs,
14665
14720
  fps
14666
14721
  );
14667
- transitionFrames[0] = aligned.from;
14668
- transitionFrames[transitionFrames.length - 1] = aligned.to;
14722
+ transitionFrames[0] = from;
14723
+ transitionFrames[transitionFrames.length - 1] = to2;
14669
14724
  return transitionFrames;
14670
14725
  } catch (error) {
14671
14726
  logger.error("[AvatarView] Failed to generate transition from idle:", error instanceof Error ? error.message : String(error));
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { b, c, f, d, j, g, C, i, D, E, k, h, L, R, S, m } from "./index-jWgogoMs.js";
1
+ import { b, c, f, d, j, g, C, i, D, E, k, h, L, R, S, m } from "./index-Bhjn1nq3.js";
2
2
  export {
3
3
  b as Avatar,
4
4
  c as AvatarController,
@@ -1,6 +1,6 @@
1
1
  import { Flame } from '../generated/driveningress/v1/driveningress';
2
2
 
3
- export declare function linearLerp(from: Flame, to: Flame, progress: number): Flame;
3
+ export declare function bezierLerp(from: Flame, to: Flame, progress: number): Flame;
4
4
 
5
5
  export declare function generateTransitionFrames(from: Flame, to: Flame, durationMs: number, fps?: number): Flame[];
6
6
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@spatialwalk/avatarkit",
3
3
  "type": "module",
4
- "version": "1.0.0-beta.61",
4
+ "version": "1.0.0-beta.62",
5
5
  "description": "SPAvatar SDK - 3D Gaussian Splatting Avatar Rendering SDK",
6
6
  "author": "SPAvatar Team",
7
7
  "license": "MIT",