Code edit (12 edits merged)
Please save this source code
User prompt
When rotating the mask, don’t restrict rotation. Stick with face tilt more closely.
User prompt
Use nose tip instead of eyes for base mask tracking. Continue to use eyes for rotation.
User prompt
Use nose instead of eyes to track mask.
User prompt
Increase the scaling bigger and smaller to more closely follow face size with the tracking of the mask and mouth animator. Do not have limits on the scale.
User prompt
Improve face tracking to track the face more closely.
User prompt
Update with: self.automatedLipSync = [ { start: 0.00, end: 5.03, value: "X" }, { start: 5.03, end: 5.16, value: "B" }, { start: 5.16, end: 5.23, value: "E" }, { start: 5.23, end: 5.30, value: "F" }, { start: 5.30, end: 5.40, value: "A" }, { start: 5.40, end: 5.54, value: "C" }, { start: 5.54, end: 5.61, value: "B" }, { start: 5.61, end: 5.82, value: "C" }, { start: 5.82, end: 5.96, value: "B" }, { start: 5.96, end: 6.03, value: "C" }, { start: 6.03, end: 6.31, value: "B" }, { start: 6.31, end: 6.52, value: "F" }, { start: 6.52, end: 6.73, value: "E" }, { start: 6.73, end: 7.43, value: "B" }, { start: 7.43, end: 9.85, value: "X" }, { start: 9.85, end: 10.03, value: "E" }, { start: 10.03, end: 10.24, value: "C" }, { start: 10.24, end: 10.38, value: "E" }, { start: 10.38, end: 10.52, value: "F" }, { start: 10.52, end: 10.59, value: "D" }, { start: 10.59, end: 11.15, value: "C" }, { start: 11.15, end: 11.23, value: "A" }, { start: 11.23, end: 11.54, value: "D" }, { start: 11.54, end: 11.62, value: "C" }, { start: 11.62, end: 12.77, value: "B" }, { start: 12.77, end: 13.77, value: "X" }, { start: 13.77, end: 13.83, value: "C" }, { start: 13.83, end: 14.44, value: "F" }, { start: 14.44, end: 14.79, value: "E" }, { start: 14.79, end: 15.01, value: "F" }, { start: 15.01, end: 15.09, value: "A" }, { start: 15.09, end: 15.13, value: "B" }, { start: 15.13, end: 15.38, value: "F" }, { start: 15.38, end: 15.45, value: "B" }, { start: 15.45, end: 16.08, value: "E" }, { start: 16.08, end: 16.29, value: "B" }, { start: 16.29, end: 17.16, value: "X" }, { start: 17.16, end: 17.23, value: "C" }, { start: 17.23, end: 17.29, value: "B" }, { start: 17.29, end: 17.99, value: "H" }, { start: 17.99, end: 19.53, value: "B" }, { start: 19.53, end: 19.86, value: "X" }, { start: 19.86, end: 20.09, value: "B" }, { start: 20.09, end: 22.08, value: "X" }, { start: 22.08, end: 22.42, value: "E" }, { start: 22.42, end: 22.77, value: "B" }, { start: 22.77, end: 22.85, value: "A" }, { start: 22.85, end: 23.02, value: "E" }, { start: 23.02, end: 23.30, value: "B" }, { start: 23.30, end: 23.65, value: "C" }, { start: 23.65, end: 23.86, value: "B" }, { start: 23.86, end: 24.49, value: "F" }, { start: 24.49, end: 24.91, value: "B" }, { start: 24.91, end: 24.98, value: "E" }, { start: 24.98, end: 25.05, value: "A" }, { start: 25.05, end: 25.33, value: "E" }, { start: 25.33, end: 25.75, value: "B" }, { start: 25.75, end: 25.82, value: "D" }, { start: 25.82, end: 25.96, value: "B" }, { start: 25.96, end: 26.17, value: "F" }, { start: 26.17, end: 26.45, value: "E" }, { start: 26.45, end: 27.08, value: "B" }, { start: 27.08, end: 27.43, value: "E" }, { start: 27.43, end: 27.57, value: "F" }, { start: 27.57, end: 27.71, value: "C" }, { start: 27.71, end: 28.48, value: "B" }, { start: 28.48, end: 32.70, value: "X" }, { start: 32.70, end: 32.82, value: "B" }, { start: 32.82, end: 33.03, value: "E" }, { start: 33.03, end: 33.45, value: "B" }, { start: 33.45, end: 33.66, value: "E" }, { start: 33.66, end: 33.73, value: "F" }, { start: 33.73, end: 33.87, value: "B" }, { start: 33.87, end: 34.01, value: "E" }, { start: 34.01, end: 34.29, value: "C" }, { start: 34.29, end: 34.36, value: "B" }, { start: 34.36, end: 34.50, value: "E" }, { start: 34.50, end: 34.71, value: "B" }, { start: 34.71, end: 34.85, value: "E" }, { start: 34.85, end: 35.13, value: "C" }, { start: 35.13, end: 35.20, value: "F" }, { start: 35.20, end: 35.48, value: "B" }, { start: 35.48, end: 35.55, value: "C" }, { start: 35.55, end: 35.76, value: "E" }, { start: 35.76, end: 35.90, value: "F" }, { start: 35.90, end: 36.60, value: "B" }, { start: 36.60, end: 36.71, value: "A" }, { start: 36.71, end: 36.89, value: "D" }, { start: 36.89, end: 36.94, value: "C" }, { start: 36.94, end: 37.47, value: "B" }, { start: 37.47, end: 37.55, value: "A" }, { start: 37.55, end: 37.65, value: "D" }, { start: 37.65, end: 37.72, value: "B" }, { start: 37.72, end: 37.92, value: "F" }, { start: 37.92, end: 38.00, value: "E" }, { start: 38.00, end: 40.10, value: "H" }, { start: 40.10, end: 43.42, value: "X" }, { start: 43.42, end: 43.67, value: "B" }, { start: 43.67, end: 43.74, value: "G" }, { start: 43.74, end: 43.88, value: "F" }, { start: 43.88, end: 44.23, value: "E" }, { start: 44.23, end: 44.44, value: "F" }, { start: 44.44, end: 44.58, value: "B" }, { start: 44.58, end: 44.79, value: "F" }, { start: 44.79, end: 45.07, value: "B" }, { start: 45.07, end: 45.35, value: "F" }, { start: 45.35, end: 45.63, value: "E" }, { start: 45.63, end: 45.70, value: "B" }, { start: 45.70, end: 46.12, value: "E" }, { start: 46.12, end: 46.40, value: "C" }, { start: 46.40, end: 46.45, value: "B" }, { start: 46.45, end: 47.13, value: "F" }, { start: 47.13, end: 47.27, value: "C" }, { start: 47.27, end: 47.34, value: "B" }, { start: 47.34, end: 47.55, value: "E" }, { start: 47.55, end: 47.76, value: "C" }, { start: 47.76, end: 47.83, value: "B" }, { start: 47.83, end: 48.04, value: "D" }, { start: 48.04, end: 48.11, value: "B" }, { start: 48.11, end: 48.81, value: "F" }, { start: 48.81, end: 48.88, value: "C" }, { start: 48.88, end: 48.95, value: "B" }, { start: 48.95, end: 49.02, value: "E" }, { start: 49.02, end: 50.28, value: "B" }, { start: 50.28, end: 50.61, value: "A" }, { start: 50.61, end: 53.74, value: "X" }, { start: 53.74, end: 54.23, value: "B" }, { start: 54.23, end: 54.31, value: "A" }, { start: 54.31, end: 54.51, value: "B" }, { start: 54.51, end: 54.79, value: "C" }, { start: 54.79, end: 54.90, value: "A" }, { start: 54.90, end: 55.01, value: "B" }, { start: 55.01, end: 55.12, value: "A" }, { start: 55.12, end: 55.18, value: "C" }, { start: 55.18, end: 55.52, value: "B" }, { start: 55.52, end: 55.59, value: "C" }, { start: 55.59, end: 55.87, value: "B" }, { start: 55.87, end: 56.22, value: "F" }, { start: 56.22, end: 56.36, value: "B" }, { start: 56.36, end: 56.66, value: "F" }, { start: 56.66, end: 56.74, value: "E" }, { start: 56.74, end: 56.89, value: "H" }, { start: 56.89, end: 57.17, value: "C" }, { start: 57.17, end: 57.69, value: "B" }, { start: 57.69, end: 57.92, value: "C" }, { start: 57.92, end: 58.20, value: "B" }, { start: 58.20, end: 59.04, value: "F" }, { start: 59.04, end: 59.18, value: "B" }, { start: 59.18, end: 59.39, value: "E" }, { start: 59.39, end: 59.53, value: "G" }, { start: 59.53, end: 59.98, value: "D" }, { start: 59.98, end: 60.06, value: "C" }, { start: 60.06, end: 60.37, value: "B" }, { start: 60.37, end: 60.51, value: "G" }, { start: 60.51, end: 60.65, value: "E" }, { start: 60.65, end: 62.19, value: "H" }, { start: 62.19, end: 62.33, value: "X" }, { start: 62.33, end: 62.55, value: "B" }, { start: 62.55, end: 63.24, value: "X" }, { start: 63.24, end: 63.38, value: "B" }, { start: 63.38, end: 63.73, value: "E" }, { start: 63.73, end: 63.87, value: "F" }, { start: 63.87, end: 63.99, value: "A" }, { start: 63.99, end: 64.11, value: "B" }, { start: 64.11, end: 64.46, value: "E" }, { start: 64.46, end: 64.60, value: "F" }, { start: 64.60, end: 64.67, value: "B" }, { start: 64.67, end: 64.88, value: "F" }, { start: 64.88, end: 64.95, value: "C" }, { start: 64.95, end: 65.88, value: "B" }, { start: 65.88, end: 66.43, value: "X" }, { start: 66.43, end: 66.62, value: "D" }, { start: 66.62, end: 66.81, value: "C" }, { start: 66.81, end: 66.86, value: "E" }, { start: 66.86, end: 67.11, value: "F" }, { start: 67.11, end: 68.93, value: "B" }, { start: 68.93, end: 69.14, value: "X" }, { start: 69.14, end: 70.99, value: "B" }, { start: 70.99, end: 71.13, value: "F" }, { start: 71.13, end: 72.63, value: "D" }, { start: 72.63, end: 72.71, value: "C" }, { start: 72.71, end: 73.16, value: "B" }, { start: 73.16, end: 75.30, value: "X" }, { start: 75.30, end: 75.57, value: "B" }, { start: 75.57, end: 75.71, value: "E" }, { start: 75.71, end: 75.99, value: "B" }, { start: 75.99, end: 76.27, value: "C" }, { start: 76.27, end: 76.34, value: "B" }, { start: 76.34, end: 76.83, value: "F" }, { start: 76.83, end: 77.04, value: "E" }, { start: 77.04, end: 77.11, value: "C" }, { start: 77.11, end: 77.18, value: "H" }, { start: 77.18, end: 77.37, value: "D" }, { start: 77.37, end: 77.42, value: "C" }, { start: 77.42, end: 77.81, value: "B" }, { start: 77.81, end: 79.14, value: "F" }, { start: 79.14, end: 79.48, value: "B" }, { start: 79.48, end: 80.18, value: "D" }, { start: 80.18, end: 80.46, value: "C" }, { start: 80.46, end: 83.12, value: "B" }, { start: 83.12, end: 84.24, value: "E" }, { start: 84.24, end: 84.45, value: "B" }, { start: 84.45, end: 85.37, value: "X" }, { start: 85.37, end: 85.55, value: "F" }, { start: 85.55, end: 86.08, value: "B" }, { start: 86.08, end: 86.62, value: "F" }, { start: 86.62, end: 86.67, value: "C" }, { start: 86.67, end: 87.00, value: "E" }, { start: 87.00, end: 87.28, value: "F" }, { start: 87.28, end: 87.42, value: "D" }, { start: 87.42, end: 87.70, value: "C" }, { start: 87.70, end: 88.54, value: "B" }, { start: 88.54, end: 89.42, value: "D" }, { start: 89.42, end: 89.49, value: "C" }, { start: 89.49, end: 89.73, value: "B" }, { start: 89.73, end: 90.46, value: "X" }, { start: 90.46, end: 90.81, value: "E" }, { start: 90.81, end: 91.27, value: "F" }, { start: 91.27, end: 91.34, value: "E" }, { start: 91.34, end: 91.41, value: "F" }, { start: 91.41, end: 92.11, value: "B" }, { start: 92.11, end: 93.07, value: "X" }, { start: 93.07, end: 93.33, value: "C" }, { start: 93.33, end: 93.40, value: "E" }, { start: 93.40, end: 93.78, value: "F" }, { start: 93.78, end: 93.85, value: "B" }, { start: 93.85, end: 94.16, value: "D" }, { start: 94.16, end: 94.24, value: "C" }, { start: 94.24, end: 95.04, value: "B" }, { start: 95.04, end: 95.79, value: "X" }, { start: 95.79, end: 95.89, value: "C" }, { start: 95.89, end: 95.96, value: "B" }, { start: 95.96, end: 96.05, value: "A" }, { start: 96.05, end: 96.31, value: "C" }, { start: 96.31, end: 96.49, value: "B" }, { start: 96.49, end: 97.18, value: "D" }, { start: 97.18, end: 97.26, value: "C" }, { start: 97.26, end: 98.43, value: "X" }, { start: 98.43, end: 98.54, value: "E" }, { start: 98.54, end: 98.61, value: "F" }, { start: 98.61, end: 98.68, value: "C" }, { start: 98.68, end: 98.75, value: "B" }, { start: 98.75, end: 98.82, value: "F" }, { start: 98.82, end: 99.10, value: "C" }, { start: 99.10, end: 99.18, value: "A" }, { start: 99.18, end: 99.64, value: "C" }, { start: 99.64, end: 99.85, value: "B" }, { start: 99.85, end: 101.13, value: "X" }, { start: 101.13, end: 101.26, value: "D" }, { start: 101.26, end: 101.33, value: "B" }, { start: 101.33, end: 101.45, value: "A" }, { start: 101.45, end: 101.86, value: "B" }, { start: 101.86, end: 102.38, value: "D" }, { start: 102.38, end: 102.46, value: "C" }, { start: 102.46, end: 103.19, value: "B" }, { start: 103.19, end: 103.47, value: "H" }, { start: 103.47, end: 104.52, value: "B" }, { start: 104.52, end: 105.15, value: "D" }, { start: 105.15, end: 105.57, value: "C" }, { start: 105.57, end: 108.41, value: "X" }, { start: 108.41, end: 108.56, value: "B" }, { start: 108.56, end: 108.63, value: "D" }, { start: 108.63, end: 108.84, value: "B" }, { start: 108.84, end: 108.98, value: "C" }, { start: 108.98, end: 109.05, value: "E" }, { start: 109.05, end: 109.33, value: "F" }, { start: 109.33, end: 109.68, value: "B" }, { start: 109.68, end: 109.82, value: "F" }, { start: 109.82, end: 109.89, value: "C" }, { start: 109.89, end: 110.03, value: "B" }, { start: 110.03, end: 110.17, value: "C" }, { start: 110.17, end: 110.24, value: "D" }, { start: 110.24, end: 110.52, value: "B" }, { start: 110.52, end: 110.59, value: "C" }, { start: 110.59, end: 111.01, value: "B" }, { start: 111.01, end: 111.08, value: "C" }, { start: 111.08, end: 111.57, value: "B" }, { start: 111.57, end: 111.64, value: "C" }, { start: 111.64, end: 111.99, value: "B" }, { start: 111.99, end: 113.77, value: "X" }, { start: 113.77, end: 114.16, value: "B" }, { start: 114.16, end: 114.65, value: "E" }, { start: 114.65, end: 114.93, value: "F" }, { start: 114.93, end: 115.00, value: "D" }, { start: 115.00, end: 115.24, value: "C" }, { start: 115.24, end: 115.46, value: "B" }, { start: 115.46, end: 115.60, value: "C" }, { start: 115.60, end: 115.74, value: "B" }, { start: 115.74, end: 115.88, value: "C" }, { start: 115.88, end: 116.09, value: "B" }, { start: 116.09, end: 116.17, value: "A" }, { start: 116.17, end: 116.24, value: "E" }, { start: 116.24, end: 116.73, value: "B" }, { start: 116.73, end: 117.57, value: "X" }, { start: 117.57, end: 117.80, value: "B" }, { start: 117.80, end: 118.81, value: "X" }, { start: 118.81, end: 119.28, value: "B" }, { start: 119.28, end: 119.35, value: "F" }, { start: 119.35, end: 119.49, value: "E" }, { start: 119.49, end: 119.65, value: "A" }, { start: 119.65, end: 119.88, value: "H" }, { start: 119.88, end: 119.95, value: "E" }, { start: 119.95, end: 120.16, value: "B" }, { start: 120.16, end: 120.30, value: "E" }, { start: 120.30, end: 120.37, value: "B" }, { start: 120.37, end: 120.65, value: "E" }, { start: 120.65, end: 120.79, value: "F" }, { start: 120.79, end: 120.93, value: "B" }, { start: 120.93, end: 121.00, value: "C" }, { start: 121.00, end: 121.28, value: "B" }, { start: 121.28, end: 121.49, value: "E" }, { start: 121.49, end: 121.56, value: "F" }, { start: 121.56, end: 121.70, value: "B" }, { start: 121.70, end: 121.98, value: "F" }, { start: 121.98, end: 122.19, value: "B" }, { start: 122.19, end: 122.61, value: "C" }, { start: 122.61, end: 123.56, value: "B" }, { start: 123.56, end: 123.77, value: "X" }, { start: 123.77, end: 123.87, value: "B" }, { start: 123.87, end: 124.02, value: "A" }, { start: 124.02, end: 124.16, value: "G" }, { start: 124.16, end: 124.30, value: "C" }, { start: 124.30, end: 124.72, value: "B" }, { start: 124.72, end: 125.00, value: "E" }, { start: 125.00, end: 125.99, value: "B" }, { start: 125.99, end: 126.06, value: "C" }, { start: 126.06, end: 126.34, value: "D" }, { start: 126.34, end: 126.48, value: "B" }, { start: 126.48, end: 126.62, value: "E" }, { start: 126.62, end: 126.69, value: "F" }, { start: 126.69, end: 126.83, value: "H" }, { start: 126.83, end: 128.26, value: "B" }, { start: 128.26, end: 128.43, value: "E" }, { start: 128.43, end: 129.70, value: "X" }, { start: 129.70, end: 129.85, value: "A" }, { start: 129.85, end: 129.96, value: "D" }, { start: 129.96, end: 130.03, value: "B" }, { start: 130.03, end: 130.17, value: "C" }, { start: 130.17, end: 130.38, value: "B" }, { start: 130.38, end: 130.52, value: "D" }, { start: 130.52, end: 130.59, value: "B" }, { start: 130.59, end: 130.87, value: "F" }, { start: 130.87, end: 131.08, value: "B" }, { start: 131.08, end: 131.15, value: "H" }, { start: 131.15, end: 131.29, value: "D" }, { start: 131.29, end: 131.36, value: "C" }, { start: 131.36, end: 131.50, value: "B" }, { start: 131.50, end: 131.81, value: "D" }, { start: 131.81, end: 131.89, value: "E" }, { start: 131.89, end: 132.20, value: "F" }, { start: 132.20, end: 132.48, value: "B" }, { start: 132.48, end: 132.62, value: "X" }, { start: 132.62, end: 132.69, value: "B" }, { start: 132.69, end: 132.90, value: "C" }, { start: 132.90, end: 133.25, value: "B" }, { start: 133.25, end: 133.45, value: "F" }, { start: 133.45, end: 133.53, value: "E" }, { start: 133.53, end: 133.88, value: "H" }, { start: 133.88, end: 134.02, value: "B" }, { start: 134.02, end: 134.14, value: "X" }, { start: 134.14, end: 134.29, value: "B" }, { start: 134.29, end: 134.36, value: "E" }, { start: 134.36, end: 134.50, value: "B" }, { start: 134.50, end: 134.71, value: "E" }, { start: 134.71, end: 134.85, value: "F" }, { start: 134.85, end: 135.06, value: "D" }, { start: 135.06, end: 135.20, value: "E" }, { start: 135.20, end: 135.27, value: "B" }, { start: 135.27, end: 135.69, value: "C" }, { start: 135.69, end: 135.83, value: "B" }, { start: 135.83, end: 136.53, value: "C" }, { start: 136.53, end: 136.67, value: "B" }, { start: 136.67, end: 140.08, value: "X" }, { start: 140.08, end: 140.22, value: "D" }, { start: 140.22, end: 140.29, value: "B" }, { start: 140.29, end: 140.57, value: "E" }, { start: 140.57, end: 141.20, value: "F" }, { start: 141.20, end: 141.41, value: "B" }, { start: 141.41, end: 141.76, value: "E" }, { start: 141.76, end: 142.11, value: "F" }, { start: 142.11, end: 142.18, value: "C" }, { start: 142.18, end: 142.25, value: "E" }, { start: 142.25, end: 142.74, value: "F" }, { start: 142.74, end: 142.93, value: "D" }, { start: 142.93, end: 142.98, value: "C" }, { start: 142.98, end: 143.21, value: "B" }, { start: 143.21, end: 143.97, value: "D" }, { start: 143.97, end: 144.11, value: "B" }, { start: 144.11, end: 144.27, value: "X" }, { start: 144.27, end: 144.40, value: "B" }, { start: 144.40, end: 145.11, value: "X" }, { start: 145.11, end: 145.58, value: "B" }, { start: 145.58, end: 145.72, value: "F" }, { start: 145.72, end: 145.80, value: "A" }, { start: 145.80, end: 145.87, value: "F" }, { start: 145.87, end: 146.18, value: "D" }, { start: 146.18, end: 146.25, value: "C" }, { start: 146.25, end: 146.49, value: "B" }, { start: 146.49, end: 146.56, value: "G" }, { start: 146.56, end: 146.77, value: "B" }, { start: 146.77, end: 146.85, value: "A" }, { start: 146.85, end: 147.26, value: "E" }, { start: 147.26, end: 147.47, value: "B" }, { start: 147.47, end: 147.75, value: "F" }, { start: 147.75, end: 147.82, value: "E" }, { start: 147.82, end: 148.15, value: "D" }, { start: 148.15, end: 148.20, value: "C" }, { start: 148.20, end: 148.38, value: "B" }, { start: 148.38, end: 148.45, value: "F" }, { start: 148.45, end: 150.48, value: "E" }, { start: 150.48, end: 150.90, value: "B" }, { start: 150.90, end: 151.41, value: "X" }, { start: 151.41, end: 151.76, value: "E" }, { start: 151.76, end: 151.90, value: "F" }, { start: 151.90, end: 152.15, value: "A" }, { start: 152.15, end: 152.40, value: "E" }, { start: 152.40, end: 152.54, value: "F" }, { start: 152.54, end: 152.61, value: "B" }, { start: 152.61, end: 152.89, value: "E" }, { start: 152.89, end: 153.13, value: "F" }, { start: 153.13, end: 153.19, value: "C" }, { start: 153.19, end: 153.38, value: "E" }, { start: 153.38, end: 153.45, value: "F" }, { start: 153.45, end: 154.69, value: "X" }, { start: 154.69, end: 154.96, value: "C" }, { start: 154.96, end: 155.16, value: "A" }, { start: 155.16, end: 155.54, value: "D" }, { start: 155.54, end: 155.62, value: "C" }, { start: 155.62, end: 156.49, value: "B" }, { start: 156.49, end: 156.63, value: "E" }, { start: 156.63, end: 157.19, value: "B" }, { start: 157.19, end: 157.47, value: "E" }, { start: 157.47, end: 157.68, value: "C" }, { start: 157.68, end: 158.18, value: "B" }, { start: 158.18, end: 158.22, value: "F" }, { start: 158.22, end: 159.21, value: "B" }, { start: 159.21, end: 159.83, value: "E" }, { start: 159.83, end: 160.25, value: "F" }, { start: 160.25, end: 160.81, value: "B" }, { start: 160.81, end: 163.32, value: "X" }, { start: 163.32, end: 163.41, value: "B" }, { start: 163.41, end: 163.49, value: "A" }, { start: 163.49, end: 163.68, value: "D" }, { start: 163.68, end: 163.73, value: "C" }, { start: 163.73, end: 163.91, value: "B" }, { start: 163.91, end: 164.12, value: "C" }, { start: 164.12, end: 164.71, value: "B" }, { start: 164.71, end: 164.92, value: "E" }, { start: 164.92, end: 165.13, value: "F" }, { start: 165.13, end: 165.48, value: "D" }, { start: 165.48, end: 165.62, value: "C" }, { start: 165.62, end: 165.90, value: "B" }, { start: 165.90, end: 165.97, value: "C" }, { start: 165.97, end: 167.23, value: "A" }, { start: 167.23, end: 167.68, value: "X" }, { start: 167.68, end: 168.61, value: "A" }, { start: 168.61, end: 168.91, value: "B" }, { start: 168.91, end: 168.98, value: "C" }, { start: 168.98, end: 169.40, value: "E" }, { start: 169.40, end: 169.73, value: "F" }, { start: 169.73, end: 172.56, value: "D" }, { start: 172.56, end: 172.64, value: "C" }, { start: 172.64, end: 173.40, value: "X" }, { start: 173.40, end: 173.53, value: "F" }, { start: 173.53, end: 174.56, value: "B" }, { start: 174.56, end: 174.63, value: "F" }, { start: 174.63, end: 174.70, value: "G" }, { start: 174.70, end: 174.98, value: "E" }, { start: 174.98, end: 175.26, value: "H" }, { start: 175.26, end: 175.68, value: "C" }, { start: 175.68, end: 176.10, value: "B" }, { start: 176.10, end: 176.24, value: "C" }, { start: 176.24, end: 176.45, value: "B" }, { start: 176.45, end: 176.52, value: "E" }, { start: 176.52, end: 177.64, value: "B" }, { start: 177.64, end: 177.85, value: "X" }, { start: 177.85, end: 177.97, value: "B" }, { start: 177.97, end: 178.27, value: "X" }, { start: 178.27, end: 178.40, value: "B" }, { start: 178.40, end: 178.54, value: "D" }, { start: 178.54, end: 178.68, value: "B" }, { start: 178.68, end: 179.17, value: "F" }, { start: 179.17, end: 179.94, value: "B" }, { start: 179.94, end: 181.13, value: "X" }, { start: 181.13, end: 181.25, value: "D" }, { start: 181.25, end: 181.39, value: "B" }, { start: 181.39, end: 181.94, value: "F" }, { start: 181.94, end: 182.33, value: "D" }, { start: 182.33, end: 182.41, value: "C" }, { start: 182.41, end: 182.86, value: "B" }, { start: 182.86, end: 182.98, value: "X" }, { start: 182.98, end: 183.17, value: "B" }, { start: 183.17, end: 183.81, value: "X" }, { start: 183.81, end: 183.96, value: "C" }, { start: 183.96, end: 184.18, value: "A" }, { start: 184.18, end: 184.63, value: "D" }, { start: 184.63, end: 184.71, value: "C" }, { start: 184.71, end: 185.44, value: "B" }, { start: 185.44, end: 186.32, value: "X" }, { start: 186.32, end: 186.49, value: "B" }, { start: 186.49, end: 186.56, value: "C" }, { start: 186.56, end: 186.63, value: "B" }, { start: 186.63, end: 186.78, value: "A" }, { start: 186.78, end: 187.00, value: "C" }, { start: 187.00, end: 188.12, value: "B" }, { start: 188.12, end: 189.10, value: "X" }, { start: 189.10, end: 189.33, value: "C" }, { start: 189.33, end: 189.47, value: "B" }, { start: 189.47, end: 189.66, value: "D" }, { start: 189.66, end: 189.71, value: "C" }, { start: 189.71, end: 190.80, value: "B" }, { start: 190.80, end: 191.22, value: "D" }, { start: 191.22, end: 191.46, value: "C" }, { start: 191.46, end: 191.50, value: "H" }, { start: 191.50, end: 191.54, value: "C" }, { start: 191.54, end: 192.52, value: "G" }, { start: 192.52, end: 192.69, value: "A" }, { start: 192.69, end: 192.83, value: "C" }, { start: 192.83, end: 193.25, value: "B" }, { start: 193.25, end: 193.50, value: "G" }, { start: 193.50, end: 193.71, value: "E" }, { start: 193.71, end: 193.82, value: "A" }, { start: 193.82, end: 194.03, value: "D" }, { start: 194.03, end: 194.09, value: "C" }, { start: 194.09, end: 195.53, value: "B" }, { start: 195.53, end: 195.77, value: "X" }, { start: 195.77, end: 195.95, value: "B" }, { start: 195.95, end: 196.65, value: "C" }, { start: 196.65, end: 198.41, value: "B" }, { start: 198.41, end: 198.66, value: "C" }, { start: 198.66, end: 198.80, value: "B" }, { start: 198.80, end: 199.08, value: "C" }, { start: 199.08, end: 199.15, value: "B" }, { start: 199.15, end: 199.43, value: "C" }, { start: 199.43, end: 199.85, value: "B" }, { start: 199.85, end: 199.99, value: "C" }, { start: 199.99, end: 200.20, value: "B" }, { start: 200.20, end: 200.41, value: "G" }, { start: 200.41, end: 201.15, value: "B" }, { start: 201.15, end: 202.25, value: "D" }, { start: 202.25, end: 202.33, value: "C" }, { start: 202.33, end: 203.09, value: "B" }, { start: 203.09, end: 203.19, value: "C" }, { start: 203.19, end: 203.26, value: "E" }, { start: 203.26, end: 203.33, value: "F" }, { start: 203.33, end: 203.89, value: "B" }, { start: 203.89, end: 206.72, value: "X" } ]; ``` ## 4. Updated Game Update Function Make sure your main game update function calls the new method: ```javascript // Game update loop (keep your existing structure, just update this part) game.update = function () { if (isGameActive) { // Update mouth animator with automated lip sync if (mouthAnimatorInstance) { mouthAnimatorInstance.update(); // This now uses updateAutomatedLipSync } // Update mask with enhanced tracking (keep your existing code) if (maskInstance) { maskInstance.update(); } } }; ↪💡 Consider importing and using the following plugins: @upit/facekit.v1
User prompt
Update with: var MouthAnimator = Container.expand(function () { var self = Container.call(this); // Load all viseme assets (keeping your existing setup) self.visemes = { closed: self.attachAsset('visemeClosed', { anchorX: 0.5, anchorY: 0.5, visible: false }), AEI: self.attachAsset('visemeAEI', { anchorX: 0.5, anchorY: 0.5, visible: false }), BMP: self.attachAsset('visemeBMP', { anchorX: 0.5, anchorY: 0.5, visible: false }), CDGKNRSTXYZ: self.attachAsset('visemeCDGKNRSTXYZ', { anchorX: 0.5, anchorY: 0.5, visible: false }), CHJSH: self.attachAsset('visemeCHJSH', { anchorX: 0.5, anchorY: 0.5, visible: false }), EE: self.attachAsset('visemeEE', { anchorX: 0.5, anchorY: 0.5, visible: false }), FV: self.attachAsset('visemeFV', { anchorX: 0.5, anchorY: 0.5, visible: false }), L: self.attachAsset('visemeL', { anchorX: 0.5, anchorY: 0.5, visible: false }), N: self.attachAsset('visemeN', { anchorX: 0.5, anchorY: 0.5, visible: false }), O: self.attachAsset('visemeO', { anchorX: 0.5, anchorY: 0.5, visible: false }), TH: self.attachAsset('visemeTH', { anchorX: 0.5, anchorY: 0.5, visible: false }), U: self.attachAsset('visemeU', { anchorX: 0.5, anchorY: 0.5, visible: false }), QW: self.attachAsset('visimeQW', { anchorX: 0.5, anchorY: 0.5, visible: false }) }; self.currentViseme = 'closed'; self.visemes.closed.visible = true; // Automated lip sync data (converted from your JSON) self.automatedLipSync = [ { start: 0.00, end: 5.03, value: "X" }, { start: 5.03, end: 5.16, value: "B" }, { start: 5.16, end: 5.23, value: "E" }, { start: 5.23, end: 5.30, value: "F" }, { start: 5.30, end: 5.40, value: "A" }, { start: 5.40, end: 5.54, value: "C" }, { start: 5.54, end: 5.61, value: "B" }, { start: 5.61, end: 5.82, value: "C" }, { start: 5.82, end: 5.96, value: "B" }, { start: 5.96, end: 6.03, value: "C" }, { start: 6.03, end: 6.31, value: "B" }, { start: 6.31, end: 6.52, value: "F" }, { start: 6.52, end: 6.73, value: "E" }, { start: 6.73, end: 7.43, value: "B" }, { start: 7.43, end: 9.85, value: "X" }, { start: 9.85, end: 10.03, value: "E" }, { start: 10.03, end: 10.24, value: "C" }, { start: 10.24, end: 10.38, value: "E" }, { start: 10.38, end: 10.52, value: "F" }, { start: 10.52, end: 10.59, value: "D" }, { start: 10.59, end: 11.15, value: "C" }, { start: 11.15, end: 11.23, value: "A" }, { start: 11.23, end: 11.54, value: "D" }, { start: 11.54, end: 11.62, value: "C" }, { start: 11.62, end: 12.77, value: "B" }, { start: 12.77, end: 13.77, value: "X" }, { start: 13.77, end: 13.83, value: "C" }, { start: 13.83, end: 14.44, value: "F" }, { start: 14.44, end: 14.79, value: "E" }, { start: 14.79, end: 15.01, value: "F" }, { start: 15.01, end: 15.09, value: "A" }, { start: 15.09, end: 15.13, value: "B" }, { start: 15.13, end: 15.38, value: "F" }, { start: 15.38, end: 15.45, value: "B" }, { start: 15.45, end: 16.08, value: "E" }, { start: 16.08, end: 16.29, value: "B" }, { start: 16.29, end: 17.16, value: "X" }, { start: 17.16, end: 17.23, value: "C" }, { start: 17.23, end: 17.29, value: "B" }, { start: 17.29, end: 17.99, value: "H" }, { start: 17.99, end: 19.53, value: "B" }, { start: 19.53, end: 19.86, value: "X" }, { start: 19.86, end: 20.09, value: "B" }, { start: 20.09, end: 22.08, value: "X" }, { start: 22.08, end: 22.42, value: "E" }, { start: 22.42, end: 22.77, value: "B" }, { start: 22.77, end: 22.85, value: "A" }, { start: 22.85, end: 23.02, value: "E" }, { start: 23.02, end: 23.30, value: "B" }, { start: 23.30, end: 23.65, value: "C" }, { start: 23.65, end: 23.86, value: "B" }, { start: 23.86, end: 24.49, value: "F" }, { start: 24.49, end: 24.91, value: "B" }, { start: 24.91, end: 24.98, value: "E" }, { start: 24.98, end: 25.05, value: "A" }, { start: 25.05, end: 25.33, value: "E" }, { start: 25.33, end: 25.75, value: "B" }, { start: 25.75, end: 25.82, value: "D" }, { start: 25.82, end: 25.96, value: "B" }, { start: 25.96, end: 26.17, value: "F" }, { start: 26.17, end: 26.45, value: "E" }, { start: 26.45, end: 27.08, value: "B" }, { start: 27.08, end: 27.43, value: "E" }, { start: 27.43, end: 27.57, value: "F" }, { start: 27.57, end: 27.71, value: "C" }, { start: 27.71, end: 28.48, value: "B" }, { start: 28.48, end: 32.70, value: "X" }, { start: 32.70, end: 32.82, value: "B" }, { start: 32.82, end: 33.03, value: "E" }, { start: 33.03, end: 33.45, value: "B" }, { start: 33.45, end: 33.66, value: "E" }, { start: 33.66, end: 33.73, value: "F" }, { start: 33.73, end: 33.87, value: "B" }, { start: 33.87, end: 34.01, value: "E" }, { start: 34.01, end: 34.29, value: "C" }, { start: 34.29, end: 34.36, value: "B" }, { start: 34.36, end: 34.50, value: "E" }, { start: 34.50, end: 34.71, value: "B" }, { start: 34.71, end: 34.85, value: "E" }, { start: 34.85, end: 35.13, value: "C" }, { start: 35.13, end: 35.20, value: "F" }, { start: 35.20, end: 35.48, value: "B" }, { start: 35.48, end: 35.55, value: "C" }, { start: 35.55, end: 35.76, value: "E" }, { start: 35.76, end: 35.90, value: "F" }, { start: 35.90, end: 36.60, value: "B" }, { start: 36.60, end: 36.71, value: "A" }, { start: 36.71, end: 36.89, value: "D" }, { start: 36.89, end: 36.94, value: "C" }, { start: 36.94, end: 37.47, value: "B" }, { start: 37.47, end: 37.55, value: "A" }, { start: 37.55, end: 37.65, value: "D" }, { start: 37.65, end: 37.72, value: "B" }, { start: 37.72, end: 37.92, value: "F" }, { start: 37.92, end: 38.00, value: "E" }, { start: 38.00, end: 40.10, value: "H" }, { start: 40.10, end: 43.42, value: "X" }, { start: 43.42, end: 43.67, value: "B" }, { start: 43.67, end: 43.74, value: "G" }, { start: 43.74, end: 43.88, value: "F" }, { start: 43.88, end: 44.23, value: "E" }, { start: 44.23, end: 44.44, value: "F" }, { start: 44.44, end: 44.58, value: "B" }, { start: 44.58, end: 44.79, value: "F" }, { start: 44.79, end: 45.07, value: "B" }, { start: 45.07, end: 45.35, value: "F" }, { start: 45.35, end: 45.63, value: "E" }, { start: 45.63, end: 45.70, value: "B" }, { start: 45.70, end: 46.12, value: "E" }, { start: 46.12, end: 46.40, value: "C" }, { start: 46.40, end: 46.45, value: "B" }, { start: 46.45, end: 47.13, value: "F" }, { start: 47.13, end: 47.27, value: "C" }, { start: 47.27, end: 47.34, value: "B" }, { start: 47.34, end: 47.55, value: "E" }, { start: 47.55, end: 47.76, value: "C" }, { start: 47.76, end: 47.83, value: "B" }, { start: 47.83, end: 48.04, value: "D" }, { start: 48.04, end: 48.11, value: "B" }, { start: 48.11, end: 48.81, value: "F" }, { start: 48.81, end: 48.88, value: "C" }, { start: 48.88, end: 48.95, value: "B" }, { start: 48.95, end: 49.02, value: "E" }, { start: 49.02, end: 50.28, value: "B" }, { start: 50.28, end: 50.61, value: "A" }, { start: 50.61, end: 53.74, value: "X" }, { start: 53.74, end: 54.23, value: "B" }, { start: 54.23, end: 54.31, value: "A" }, { start: 54.31, end: 54.51, value: "B" }, { start: 54.51, end: 54.79, value: "C" }, { start: 54.79, end: 54.90, value: "A" }, { start: 54.90, end: 55.01, value: "B" }, { start: 55.01, end: 55.12, value: "A" }, { start: 55.12, end: 55.18, value: "C" }, { start: 55.18, end: 55.52, value: "B" }, { start: 55.52, end: 55.59, value: "C" }, { start: 55.59, end: 55.87, value: "B" }, { start: 55.87, end: 56.22, value: "F" }, { start: 56.22, end: 56.36, value: "B" }, { start: 56.36, end: 56.66, value: "F" }, { start: 56.66, end: 56.74, value: "E" }, { start: 56.74, end: 56.89, value: "H" }, { start: 56.89, end: 57.17, value: "C" }, { start: 57.17, end: 57.69, value: "B" }, { start: 57.69, end: 57.92, value: "C" }, { start: 57.92, end: 58.20, value: "B" }, { start: 58.20, end: 59.04, value: "F" }, { start: 59.04, end: 59.18, value: "B" }, { start: 59.18, end: 59.39, value: "E" }, { start: 59.39, end: 59.53, value: "G" }, { start: 59.53, end: 59.98, value: "D" }, { start: 59.98, end: 60.06, value: "C" }, { start: 60.06, end: 60.37, value: "B" }, { start: 60.37, end: 60.51, value: "G" }, { start: 60.51, end: 60.65, value: "E" }, { start: 60.65, end: 62.19, value: "H" }, { start: 62.19, end: 62.33, value: "X" }, { start: 62.33, end: 62.55, value: "B" }, { start: 62.55, end: 63.24, value: "X" }, { start: 63.24, end: 63.38, value: "B" }, { start: 63.38, end: 63.73, value: "E" }, { start: 63.73, end: 63.87, value: "F" }, { start: 63.87, end: 63.99, value: "A" }, { start: 63.99, end: 64.11, value: "B" }, { start: 64.11, end: 64.46, value: "E" }, { start: 64.46, end: 64.60, value: "F" }, { start: 64.60, end: 64.67, value: "B" }, { start: 64.67, end: 64.88, value: "F" }, { start: 64.88, end: 64.95, value: "C" }, { start: 64.95, end: 65.88, value: "B" }, { start: 65.88, end: 66.43, value: "X" }, { start: 66.43, end: 66.62, value: "D" }, { start: 66.62, end: 66.81, value: "C" }, { start: 66.81, end: 66.86, value: "E" }, { start: 66.86, end: 67.11, value: "F" }, { start: 67.11, end: 68.93, value: "B" }, { start: 68.93, end: 69.14, value: "X" }, { start: 69.14, end: 70.99, value: "B" }, { start: 70.99, end: 71.13, value: "F" }, { start: 71.13, end: 72.63, value: "D" }, { start: 72.63, end: 72.71, value: "C" }, { start: 72.71, end: 73.16, value: "B" }, { start: 73.16, end: 75.30, value: "X" }, { start: 75.30, end: 75.57, value: "B" }, { start: 75.57, end: 75.71, value: "E" }, { start: 75.71, end: 75.99, value: "B" }, { start: 75.99, end: 76.27, value: "C" }, { start: 76.27, end: 76.34, value: "B" }, { start: 76.34, end: 76.83, value: "F" }, { start: 76.83, end: 77.04, value: "E" }, { start: 77.04, end: 77.11, value: "C" }, { start: 77.11, end: 77.18, value: "H" }, { start: 77.18, end: 77.37, value: "D" }, { start: 77.37, end: 77.42, value: "C" }, { start: 77.42, end: 77.81, value: "B" }, { start: 77.81, end: 79.14, value: "F" }, { start: 79.14, end: 79.48, value: "B" }, { start: 79.48, end: 80.18, value: "D" }, { start: 80.18, end: 80.46, value: "C" }, { start: 80.46, end: 83.12, value: "B" }, { start: 83.12, end: 84.24, value: "E" }, { start: 84.24, end: 84.45, value: "B" }, { start: 84.45, end: 85.37, value: "X" }, { start: 85.37, end: 85.55, value: "F" }, { start: 85.55, end: 86.08, value: "B" }, { start: 86.08, end: 86.62, value: "F" }, { start: 86.62, end: 86.67, value: "C" }, { start: 86.67, end: 87.00, value: "E" }, { start: 87.00, end: 87.28, value: "F" }, { start: 87.28, end: 87.42, value: "D" }, { start: 87.42, end: 87.70, value: "C" }, { start: 87.70, end: 88.54, value: "B" }, { start: 88.54, end: 89.42, value: "D" }, { start: 89.42, end: 89.49, value: "C" }, { start: 89.49, end: 89.73, value: "B" }, { start: 89.73, end: 90.46, value: "X" }, { start: 90.46, end: 90.81, value: "E" }, { start: 90.81, end: 91.27, value: "F" }, { start: 91.27, end: 91.34, value: "E" }, { start: 91.34, end: 91.41, value: "F" }, { start: 91.41, end: 92.11, value: "B" }, { start: 92.11, end: 93.07, value: "X" }, { start: 93.07, end: 93.33, value: "C" }, { start: 93.33, end: 93.40, value: "E" }, { start: 93.40, end: 93.78, value: "F" }, { start: 93.78, end: 93.85, value: "B" }, { start: 93.85, end: 94.16, value: "D" }, { start: 94.16, end: 94.24, value: "C" }, { start: 94.24, end: 95.04, value: "B" }, { start: 95.04, end: 95.79, value: "X" }, { start: 95.79, end: 95.89, value: "C" }, { start: 95.89, end: 95.96, value: "B" }, { start: 95.96, end: 96.05, value: "A" }, { start: 96.05, end: 96.31, value: "C" }, { start: 96.31, end: 96.49, value: "B" }, { start: 96.49, end: 97.18, value: "D" }, { start: 97.18, end: 97.26, value: "C" }, { start: 97.26, end: 98.43, value: "X" }, { start: 98.43, end: 98.54, value: "E" }, { start: 98.54, end: 98.61, value: "F" }, { start: 98.61, end: 98.68, value: "C" }, { start: 98.68, end: 98.75, value: "B" }, { start: 98.75, end: 98.82, value: "F" }, { start: 98.82, end: 99.10, value: "C" }, { start: 99.10, end: 99.18, value: "A" }, { start: 99.18, end: 99.64, value: "C" }, { start: 99.64, end: 99.85, value: "B" }, { start: 99.85, end: 101.13, value: "X" }, { start: 101.13, end: 101.26, value: "D" }, { start: 101.26, end: 101.33, value: "B" }, { start: 101.33, end: 101.45, value: "A" }, { start: 101.45, end: 101.86, value: "B" }, { start: 101.86, end: 102.38, value: "D" }, { start: 102.38, end: 102.46, value: "C" }, { start: 102.46, end: 103.19, value: "B" }, { start: 103.19, end: 103.47, value: "H" }, { start: 103.47, end: 104.52, value: "B" }, { start: 104.52, end: 105.15, value: "D" }, { start: 105.15, end: 105.57, value: "C" }, // ... continuing with all entries from your JSON data // (I've included the first portion - you'll need to add the rest) ]; // Viseme mapping self.visemeMapping = { 'X': 'closed', 'A': 'AEI', 'B': 'BMP', 'C': 'CDGKNRSTXYZ', 'D': 'AEI', 'E': 'EE', 'F': 'FV', 'G': 'CDGKNRSTXYZ', 'H': 'CHJSH' }; self.songStartTime = null; self.isLipSyncActive = false; self.currentLipSyncIndex = 0; // Position tracking variables (keeping your existing setup) var targetX = GAME_WIDTH / 2; var targetY = GAME_HEIGHT / 2 + 200; var smoothingFactor = 0.18; var prevX = null; var prevY = null; // Scale tracking variables (keeping your existing setup) var scaleHistory = new Array(5).fill(1); var scaleIndex = 0; var currentScale = 1; // Rotation variables (keeping your existing setup) var targetTilt = 0; var tiltSmoothingFactor = 0.11; var tiltScaleFactor = 0.09; function calculateFaceTilt() { if (facekit.leftEye && facekit.rightEye && facekit.mouthCenter) { var eyeMidX = (facekit.leftEye.x + facekit.rightEye.x) / 2; var eyeMidY = (facekit.leftEye.y + facekit.rightEye.y) / 2; var dx = facekit.mouthCenter.x - eyeMidX; var dy = facekit.mouthCenter.y - eyeMidY; var angle = -(Math.atan2(dx, dy) * (180 / Math.PI)); return Math.max(-15, Math.min(15, angle * 0.15)); } return 0; } self.startLipSync = function () { self.songStartTime = Date.now(); self.isLipSyncActive = true; self.currentLipSyncIndex = 0; }; self.setViseme = function (visemeName) { if (self.currentViseme !== visemeName && self.visemes[visemeName]) { // Hide current viseme if (self.visemes[self.currentViseme]) { self.visemes[self.currentViseme].visible = false; } // Show new viseme self.visemes[visemeName].visible = true; self.currentViseme = visemeName; } }; self.updateAutomatedLipSync = function () { if (!self.isLipSyncActive || !self.songStartTime) { return; } var currentTimeSeconds = (Date.now() - self.songStartTime) / 1000.0; // Find the current viseme based on time var targetViseme = 'closed'; for (var i = 0; i < self.automatedLipSync.length; i++) { var cue = self.automatedLipSync[i]; if (currentTimeSeconds >= cue.start && currentTimeSeconds < cue.end) { // Map the automated code to your viseme asset var automatedCode = cue.value; var mappedViseme = self.visemeMapping[automatedCode] || 'closed'; targetViseme = mappedViseme; break; } } self.setViseme(targetViseme); }; self.update = function () { // Update automated lip sync self.updateAutomatedLipSync(); // Smooth position tracking (keeping your existing code) if (facekit.mouthCenter) { targetX = facekit.mouthCenter.x; targetY = facekit.mouthCenter.y; if (prevX === null) { prevX = targetX; prevY = targetY; } var newX = prevX * (1 - smoothingFactor) + targetX * smoothingFactor; var newY = prevY * (1 - smoothingFactor) + targetY * smoothingFactor; self.x = newX; self.y = newY;prevX = newX; prevY = newY; } // Dynamic scale adjustment (keeping your existing code) if (facekit.leftEye && facekit.rightEye) { var eyeDistance = Math.abs(facekit.rightEye.x - facekit.leftEye.x); var newScale = eyeDistance / 250; scaleHistory[scaleIndex] = newScale; scaleIndex = (scaleIndex + 1) % scaleHistory.length; var avgScale = scaleHistory.reduce(function (a, b) { return a + b; }, 0) / scaleHistory.length; var targetScale = Math.max(0.6, Math.min(1.4, avgScale)); currentScale = currentScale * 0.85 + targetScale * 0.15; // Apply scale to all visemes Object.keys(self.visemes).forEach(function (key) { self.visemes[key].scaleX = currentScale; self.visemes[key].scaleY = currentScale; }); } // Rotation tracking (keeping your existing code) if (facekit.leftEye && facekit.rightEye) { targetTilt = calculateFaceTilt() * tiltScaleFactor; targetTilt = Math.max(-15, Math.min(15, targetTilt)); self.rotation += (targetTilt - self.rotation) * tiltSmoothingFactor; } }; return self; }); ↪💡 Consider importing and using the following plugins: @upit/facekit.v1
User prompt
Update with: var MouthAnimator = Container.expand(function () { var self = Container.call(this); // Load all viseme assets (keeping your existing setup) self.visemes = { closed: self.attachAsset('visemeClosed', { anchorX: 0.5, anchorY: 0.5, visible: false }), AEI: self.attachAsset('visemeAEI', { anchorX: 0.5, anchorY: 0.5, visible: false }), BMP: self.attachAsset('visemeBMP', { anchorX: 0.5, anchorY: 0.5, visible: false }), CDGKNRSTXYZ: self.attachAsset('visemeCDGKNRSTXYZ', { anchorX: 0.5, anchorY: 0.5, visible: false }), CHJSH: self.attachAsset('visemeCHJSH', { anchorX: 0.5, anchorY: 0.5, visible: false }), EE: self.attachAsset('visemeEE', { anchorX: 0.5, anchorY: 0.5, visible: false }), FV: self.attachAsset('visemeFV', { anchorX: 0.5, anchorY: 0.5, visible: false }), L: self.attachAsset('visemeL', { anchorX: 0.5, anchorY: 0.5, visible: false }), N: self.attachAsset('visemeN', { anchorX: 0.5, anchorY: 0.5, visible: false }), O: self.attachAsset('visemeO', { anchorX: 0.5, anchorY: 0.5, visible: false }), TH: self.attachAsset('visemeTH', { anchorX: 0.5, anchorY: 0.5, visible: false }), U: self.attachAsset('visemeU', { anchorX: 0.5, anchorY: 0.5, visible: false }), QW: self.attachAsset('visimeQW', { anchorX: 0.5, anchorY: 0.5, visible: false }) }; self.currentViseme = 'closed'; self.visemes.closed.visible = true; // Automated lip sync data (converted from your JSON) self.automatedLipSync = [ { start: 0.00, end: 5.03, value: "X" }, { start: 5.03, end: 5.16, value: "B" }, { start: 5.16, end: 5.23, value: "E" }, { start: 5.23, end: 5.30, value: "F" }, { start: 5.30, end: 5.40, value: "A" }, { start: 5.40, end: 5.54, value: "C" }, { start: 5.54, end: 5.61, value: "B" }, { start: 5.61, end: 5.82, value: "C" }, { start: 5.82, end: 5.96, value: "B" }, { start: 5.96, end: 6.03, value: "C" }, { start: 6.03, end: 6.31, value: "B" }, { start: 6.31, end: 6.52, value: "F" }, { start: 6.52, end: 6.73, value: "E" }, { start: 6.73, end: 7.43, value: "B" }, { start: 7.43, end: 9.85, value: "X" }, { start: 9.85, end: 10.03, value: "E" }, { start: 10.03, end: 10.24, value: "C" }, { start: 10.24, end: 10.38, value: "E" }, { start: 10.38, end: 10.52, value: "F" }, { start: 10.52, end: 10.59, value: "D" }, { start: 10.59, end: 11.15, value: "C" }, { start: 11.15, end: 11.23, value: "A" }, { start: 11.23, end: 11.54, value: "D" }, { start: 11.54, end: 11.62, value: "C" }, { start: 11.62, end: 12.77, value: "B" }, { start: 12.77, end: 13.77, value: "X" }, { start: 13.77, end: 13.83, value: "C" }, { start: 13.83, end: 14.44, value: "F" }, { start: 14.44, end: 14.79, value: "E" }, { start: 14.79, end: 15.01, value: "F" }, { start: 15.01, end: 15.09, value: "A" }, { start: 15.09, end: 15.13, value: "B" }, { start: 15.13, end: 15.38, value: "F" }, { start: 15.38, end: 15.45, value: "B" }, { start: 15.45, end: 16.08, value: "E" }, { start: 16.08, end: 16.29, value: "B" }, { start: 16.29, end: 17.16, value: "X" }, { start: 17.16, end: 17.23, value: "C" }, { start: 17.23, end: 17.29, value: "B" }, { start: 17.29, end: 17.99, value: "H" }, { start: 17.99, end: 19.53, value: "B" }, { start: 19.53, end: 19.86, value: "X" }, { start: 19.86, end: 20.09, value: "B" }, { start: 20.09, end: 22.08, value: "X" }, { start: 22.08, end: 22.42, value: "E" }, { start: 22.42, end: 22.77, value: "B" }, { start: 22.77, end: 22.85, value: "A" }, { start: 22.85, end: 23.02, value: "E" }, { start: 23.02, end: 23.30, value: "B" }, { start: 23.30, end: 23.65, value: "C" }, { start: 23.65, end: 23.86, value: "B" }, { start: 23.86, end: 24.49, value: "F" }, { start: 24.49, end: 24.91, value: "B" }, { start: 24.91, end: 24.98, value: "E" }, { start: 24.98, end: 25.05, value: "A" }, { start: 25.05, end: 25.33, value: "E" }, { start: 25.33, end: 25.75, value: "B" }, { start: 25.75, end: 25.82, value: "D" }, { start: 25.82, end: 25.96, value: "B" }, { start: 25.96, end: 26.17, value: "F" }, { start: 26.17, end: 26.45, value: "E" }, { start: 26.45, end: 27.08, value: "B" }, { start: 27.08, end: 27.43, value: "E" }, { start: 27.43, end: 27.57, value: "F" }, { start: 27.57, end: 27.71, value: "C" }, { start: 27.71, end: 28.48, value: "B" }, { start: 28.48, end: 32.70, value: "X" }, { start: 32.70, end: 32.82, value: "B" }, { start: 32.82, end: 33.03, value: "E" }, { start: 33.03, end: 33.45, value: "B" }, { start: 33.45, end: 33.66, value: "E" }, { start: 33.66, end: 33.73, value: "F" }, { start: 33.73, end: 33.87, value: "B" }, { start: 33.87, end: 34.01, value: "E" }, { start: 34.01, end: 34.29, value: "C" }, { start: 34.29, end: 34.36, value: "B" }, { start: 34.36, end: 34.50, value: "E" }, { start: 34.50, end: 34.71, value: "B" }, { start: 34.71, end: 34.85, value: "E" }, { start: 34.85, end: 35.13, value: "C" }, { start: 35.13, end: 35.20, value: "F" }, { start: 35.20, end: 35.48, value: "B" }, { start: 35.48, end: 35.55, value: "C" }, { start: 35.55, end: 35.76, value: "E" }, { start: 35.76, end: 35.90, value: "F" }, { start: 35.90, end: 36.60, value: "B" }, { start: 36.60, end: 36.71, value: "A" }, { start: 36.71, end: 36.89, value: "D" }, { start: 36.89, end: 36.94, value: "C" }, { start: 36.94, end: 37.47, value: "B" }, { start: 37.47, end: 37.55, value: "A" }, { start: 37.55, end: 37.65, value: "D" }, { start: 37.65, end: 37.72, value: "B" }, { start: 37.72, end: 37.92, value: "F" }, { start: 37.92, end: 38.00, value: "E" }, { start: 38.00, end: 40.10, value: "H" }, { start: 40.10, end: 43.42, value: "X" }, { start: 43.42, end: 43.67, value: "B" }, { start: 43.67, end: 43.74, value: "G" }, { start: 43.74, end: 43.88, value: "F" }, { start: 43.88, end: 44.23, value: "E" }, { start: 44.23, end: 44.44, value: "F" }, { start: 44.44, end: 44.58, value: "B" }, { start: 44.58, end: 44.79, value: "F" }, { start: 44.79, end: 45.07, value: "B" }, { start: 45.07, end: 45.35, value: "F" }, { start: 45.35, end: 45.63, value: "E" }, { start: 45.63, end: 45.70, value: "B" }, { start: 45.70, end: 46.12, value: "E" }, { start: 46.12, end: 46.40, value: "C" }, { start: 46.40, end: 46.45, value: "B" }, { start: 46.45, end: 47.13, value: "F" }, { start: 47.13, end: 47.27, value: "C" }, { start: 47.27, end: 47.34, value: "B" }, { start: 47.34, end: 47.55, value: "E" }, { start: 47.55, end: 47.76, value: "C" }, { start: 47.76, end: 47.83, value: "B" }, { start: 47.83, end: 48.04, value: "D" }, { start: 48.04, end: 48.11, value: "B" }, { start: 48.11, end: 48.81, value: "F" }, { start: 48.81, end: 48.88, value: "C" }, { start: 48.88, end: 48.95, value: "B" }, { start: 48.95, end: 49.02, value: "E" }, { start: 49.02, end: 50.28, value: "B" }, { start: 50.28, end: 50.61, value: "A" }, { start: 50.61, end: 53.74, value: "X" }, { start: 53.74, end: 54.23, value: "B" }, { start: 54.23, end: 54.31, value: "A" }, { start: 54.31, end: 54.51, value: "B" }, { start: 54.51, end: 54.79, value: "C" }, { start: 54.79, end: 54.90, value: "A" }, { start: 54.90, end: 55.01, value: "B" }, { start: 55.01, end: 55.12, value: "A" }, { start: 55.12, end: 55.18, value: "C" }, { start: 55.18, end: 55.52, value: "B" }, { start: 55.52, end: 55.59, value: "C" }, { start: 55.59, end: 55.87, value: "B" }, { start: 55.87, end: 56.22, value: "F" }, { start: 56.22, end: 56.36, value: "B" }, { start: 56.36, end: 56.66, value: "F" }, { start: 56.66, end: 56.74, value: "E" }, { start: 56.74, end: 56.89, value: "H" }, { start: 56.89, end: 57.17, value: "C" }, { start: 57.17, end: 57.69, value: "B" }, { start: 57.69, end: 57.92, value: "C" }, { start: 57.92, end: 58.20, value: "B" }, { start: 58.20, end: 59.04, value: "F" }, { start: 59.04, end: 59.18, value: "B" }, { start: 59.18, end: 59.39, value: "E" }, { start: 59.39, end: 59.53, value: "G" }, { start: 59.53, end: 59.98, value: "D" }, { start: 59.98, end: 60.06, value: "C" }, { start: 60.06, end: 60.37, value: "B" }, { start: 60.37, end: 60.51, value: "G" }, { start: 60.51, end: 60.65, value: "E" }, { start: 60.65, end: 62.19, value: "H" }, { start: 62.19, end: 62.33, value: "X" }, { start: 62.33, end: 62.55, value: "B" }, { start: 62.55, end: 63.24, value: "X" }, { start: 63.24, end: 63.38, value: "B" }, { start: 63.38, end: 63.73, value: "E" }, { start: 63.73, end: 63.87, value: "F" }, { start: 63.87, end: 63.99, value: "A" }, { start: 63.99, end: 64.11, value: "B" }, { start: 64.11, end: 64.46, value: "E" }, { start: 64.46, end: 64.60, value: "F" }, { start: 64.60, end: 64.67, value: "B" }, { start: 64.67, end: 64.88, value: "F" }, { start: 64.88, end: 64.95, value: "C" }, { start: 64.95, end: 65.88, value: "B" }, { start: 65.88, end: 66.43, value: "X" }, { start: 66.43, end: 66.62, value: "D" }, { start: 66.62, end: 66.81, value: "C" }, { start: 66.81, end: 66.86, value: "E" }, { start: 66.86, end: 67.11, value: "F" }, { start: 67.11, end: 68.93, value: "B" }, { start: 68.93, end: 69.14, value: "X" }, { start: 69.14, end: 70.99, value: "B" }, { start: 70.99, end: 71.13, value: "F" }, { start: 71.13, end: 72.63, value: "D" }, { start: 72.63, end: 72.71, value: "C" }, { start: 72.71, end: 73.16, value: "B" }, { start: 73.16, end: 75.30, value: "X" }, { start: 75.30, end: 75.57, value: "B" }, { start: 75.57, end: 75.71, value: "E" }, { start: 75.71, end: 75.99, value: "B" }, { start: 75.99, end: 76.27, value: "C" }, { start: 76.27, end: 76.34, value: "B" }, { start: 76.34, end: 76.83, value: "F" }, { start: 76.83, end: 77.04, value: "E" }, { start: 77.04, end: 77.11, value: "C" }, { start: 77.11, end: 77.18, value: "H" }, { start: 77.18, end: 77.37, value: "D" }, { start: 77.37, end: 77.42, value: "C" }, { start: 77.42, end: 77.81, value: "B" }, { start: 77.81, end: 79.14, value: "F" }, { start: 79.14, end: 79.48, value: "B" }, { start: 79.48, end: 80.18, value: "D" }, { start: 80.18, end: 80.46, value: "C" }, { start: 80.46, end: 83.12, value: "B" }, { start: 83.12, end: 84.24, value: "E" }, { start: 84.24, end: 84.45, value: "B" }, { start: 84.45, end: 85.37, value: "X" }, { start: 85.37, end: 85.55, value: "F" }, { start: 85.55, end: 86.08, value: "B" }, { start: 86.08, end: 86.62, value: "F" }, { start: 86.62, end: 86.67, value: "C" }, { start: 86.67, end: 87.00, value: "E" }, { start: 87.00, end: 87.28, value: "F" }, { start: 87.28, end: 87.42, value: "D" }, { start: 87.42, end: 87.70, value: "C" }, { start: 87.70, end: 88.54, value: "B" }, { start: 88.54, end: 89.42, value: "D" }, { start: 89.42, end: 89.49, value: "C" }, { start: 89.49, end: 89.73, value: "B" }, { start: 89.73, end: 90.46, value: "X" }, { start: 90.46, end: 90.81, value: "E" }, { start: 90.81, end: 91.27, value: "F" }, { start: 91.27, end: 91.34, value: "E" }, { start: 91.34, end: 91.41, value: "F" }, { start: 91.41, end: 92.11, value: "B" }, { start: 92.11, end: 93.07, value: "X" }, { start: 93.07, end: 93.33, value: "C" }, { start: 93.33, end: 93.40, value: "E" }, { start: 93.40, end: 93.78, value: "F" }, { start: 93.78, end: 93.85, value: "B" }, { start: 93.85, end: 94.16, value: "D" }, { start: 94.16, end: 94.24, value: "C" }, { start: 94.24, end: 95.04, value: "B" }, { start: 95.04, end: 95.79, value: "X" }, { start: 95.79, end: 95.89, value: "C" }, { start: 95.89, end: 95.96, value: "B" }, { start: 95.96, end: 96.05, value: "A" }, { start: 96.05, end: 96.31, value: "C" }, { start: 96.31, end: 96.49, value: "B" }, { start: 96.49, end: 97.18, value: "D" }, { start: 97.18, end: 97.26, value: "C" }, { start: 97.26, end: 98.43, value: "X" }, { start: 98.43, end: 98.54, value: "E" }, { start: 98.54, end: 98.61, value: "F" }, { start: 98.61, end: 98.68, value: "C" }, { start: 98.68, end: 98.75, value: "B" }, { start: 98.75, end: 98.82, value: "F" }, { start: 98.82, end: 99.10, value: "C" }, { start: 99.10, end: 99.18, value: "A" }, { start: 99.18, end: 99.64, value: "C" }, { start: 99.64, end: 99.85, value: "B" }, { start: 99.85, end: 101.13, value: "X" }, { start: 101.13, end: 101.26, value: "D" }, { start: 101.26, end: 101.33, value: "B" }, { start: 101.33, end: 101.45, value: "A" }, { start: 101.45, end: 101.86, value: "B" }, { start: 101.86, end: 102.38, value: "D" }, { start: 102.38, end: 102.46, value: "C" }, { start: 102.46, end: 103.19, value: "B" }, { start: 103.19, end: 103.47, value: "H" }, { start: 103.47, end: 104.52, value: "B" }, { start: 104.52, end: 105.15, value: "D" }, { start: 105.15, end: 105.57, value: "C" }, // ... continuing with all entries from your JSON data // (I've included the first portion - you'll need to add the rest) ]; // Viseme mapping self.visemeMapping = { 'X': 'closed', 'A': 'AEI', 'B': 'BMP', 'C': 'CDGKNRSTXYZ', 'D': 'AEI', 'E': 'EE', 'F': 'FV', 'G': 'CDGKNRSTXYZ', 'H': 'CHJSH' }; self.songStartTime = null; self.isLipSyncActive = false; self.currentLipSyncIndex = 0; // Position tracking variables (keeping your existing setup) var targetX = GAME_WIDTH / 2; var targetY = GAME_HEIGHT / 2 + 200; var smoothingFactor = 0.18; var prevX = null; var prevY = null; // Scale tracking variables (keeping your existing setup) var scaleHistory = new Array(5).fill(1); var scaleIndex = 0; var currentScale = 1; // Rotation variables (keeping your existing setup) var targetTilt = 0; var tiltSmoothingFactor = 0.11; var tiltScaleFactor = 0.09; function calculateFaceTilt() { if (facekit.leftEye && facekit.rightEye && facekit.mouthCenter) { var eyeMidX = (facekit.leftEye.x + facekit.rightEye.x) / 2; var eyeMidY = (facekit.leftEye.y + facekit.rightEye.y) / 2; var dx = facekit.mouthCenter.x - eyeMidX; var dy = facekit.mouthCenter.y - eyeMidY; var angle = -(Math.atan2(dx, dy) * (180 / Math.PI)); return Math.max(-15, Math.min(15, angle * 0.15)); } return 0; } self.startLipSync = function () { self.songStartTime = Date.now(); self.isLipSyncActive = true; self.currentLipSyncIndex = 0; }; self.setViseme = function (visemeName) { if (self.currentViseme !== visemeName && self.visemes[visemeName]) { // Hide current viseme if (self.visemes[self.currentViseme]) { self.visemes[self.currentViseme].visible = false; } // Show new viseme self.visemes[visemeName].visible = true; self.currentViseme = visemeName; } }; self.updateAutomatedLipSync = function () { if (!self.isLipSyncActive || !self.songStartTime) { return; } var currentTimeSeconds = (Date.now() - self.songStartTime) / 1000.0; // Find the current viseme based on time var targetViseme = 'closed'; for (var i = 0; i < self.automatedLipSync.length; i++) { var cue = self.automatedLipSync[i]; if (currentTimeSeconds >= cue.start && currentTimeSeconds < cue.end) { // Map the automated code to your viseme asset var automatedCode = cue.value; var mappedViseme = self.visemeMapping[automatedCode] || 'closed'; targetViseme = mappedViseme; break; } } self.setViseme(targetViseme); }; self.update = function () { // Update automated lip sync self.updateAutomatedLipSync(); // Smooth position tracking (keeping your existing code) if (facekit.mouthCenter) { targetX = facekit.mouthCenter.x; targetY = facekit.mouthCenter.y; if (prevX === null) { prevX = targetX; prevY = targetY; } var newX = prevX * (1 - smoothingFactor) + targetX * smoothingFactor; var newY = prevY * (1 - smoothingFactor) + targetY * smoothingFactor; self.x = newX; self.y = newY;prevX = newX; prevY = newY; } // Dynamic scale adjustment (keeping your existing code) if (facekit.leftEye && facekit.rightEye) { var eyeDistance = Math.abs(facekit.rightEye.x - facekit.leftEye.x); var newScale = eyeDistance / 250; scaleHistory[scaleIndex] = newScale; scaleIndex = (scaleIndex + 1) % scaleHistory.length; var avgScale = scaleHistory.reduce(function (a, b) { return a + b; }, 0) / scaleHistory.length; var targetScale = Math.max(0.6, Math.min(1.4, avgScale)); currentScale = currentScale * 0.85 + targetScale * 0.15; // Apply scale to all visemes Object.keys(self.visemes).forEach(function (key) { self.visemes[key].scaleX = currentScale; self.visemes[key].scaleY = currentScale; }); } // Rotation tracking (keeping your existing code) if (facekit.leftEye && facekit.rightEye) { targetTilt = calculateFaceTilt() * tiltScaleFactor; targetTilt = Math.max(-15, Math.min(15, targetTilt)); self.rotation += (targetTilt - self.rotation) * tiltSmoothingFactor; } }; return self; });
Code edit (2 edits merged)
Please save this source code
User prompt
Update with: self.lipSyncTimeline = [ // "From the ashes of tragedy" { time: 5500, viseme: 'FV' }, // "From" { time: 6000, viseme: 'TH' }, // "the" { time: 6200, viseme: 'AEI' }, // "ashes" { time: 6800, viseme: 'O' }, // "of" { time: 7000, viseme: 'CDGKNRSTXYZ' }, // "tragedy" { time: 7500, viseme: 'closed' }, // "A guardian rises" { time: 10000, viseme: 'AEI' }, // "A" { time: 10200, viseme: 'CDGKNRSTXYZ' }, // "guardian" { time: 11500, viseme: 'CDGKNRSTXYZ' }, // "rises" { time: 12500, viseme: 'closed' }, // "Not the hero they deserve" { time: 14000, viseme: 'N' }, // "Not" { time: 14300, viseme: 'TH' }, // "the" { time: 14600, viseme: 'EE' }, // "hero" { time: 15200, viseme: 'TH' }, // "they" { time: 15600, viseme: 'CDGKNRSTXYZ' }, // "deserve" { time: 16000, viseme: 'closed' }, // "But the one they need" { time: 17500, viseme: 'BMP' }, // "But" { time: 17800, viseme: 'TH' }, // "the" { time: 18100, viseme: 'QW' }, // "one" { time: 18500, viseme: 'TH' }, // "they" { time: 18800, viseme: 'N' }, // "need" { time: 19500, viseme: 'closed' }, // "Parents fallen in an alley cold" { time: 22500, viseme: 'BMP' }, // "Parents" { time: 23000, viseme: 'FV' }, // "fallen" { time: 23500, viseme: 'AEI' }, // "in" { time: 23800, viseme: 'AEI' }, // "an" { time: 24000, viseme: 'AEI' }, // "alley" { time: 24500, viseme: 'CDGKNRSTXYZ' }, // "cold" { time: 25000, viseme: 'closed' }, // "A boys heart turned to stone" { time: 25000, viseme: 'AEI' }, // "A" { time: 25300, viseme: 'BMP' }, // "boys" { time: 26000, viseme: 'CDGKNRSTXYZ' }, // "heart" { time: 26800, viseme: 'CDGKNRSTXYZ' }, // "turned" { time: 27500, viseme: 'CDGKNRSTXYZ' }, // "to" { time: 28000, viseme: 'CDGKNRSTXYZ' }, // "stone" { time: 29000, viseme: 'closed' }, // "Fortune built on others' pain" { time: 33000, viseme: 'FV' }, // "Fortune" { time: 33600, viseme: 'BMP' }, // "built" { time: 34000, viseme: 'O' }, // "on" { time: 34300, viseme: 'O' }, // "others'" { time: 34800, viseme: 'BMP' }, // "pain" { time: 35000, viseme: 'closed' }, // "Now I claim the night as my own" { time: 36000, viseme: 'N' }, // "Now" { time: 36300, viseme: 'AEI' }, // "I" { time: 36600, viseme: 'CDGKNRSTXYZ' }, // "claim" { time: 37000, viseme: 'TH' }, // "the" { time: 37300, viseme: 'N' }, // "night" { time: 37800, viseme: 'AEI' }, // "as" { time: 38000, viseme: 'BMP' }, // "my" { time: 38500, viseme: 'O' }, // "own" { time: 40000, viseme: 'closed' }, // "Gotham calls to me in whispers dire" { time: 43500, viseme: 'CDGKNRSTXYZ' }, // "Gotham" { time: 44000, viseme: 'CDGKNRSTXYZ' }, // "calls" { time: 44500, viseme: 'CDGKNRSTXYZ' }, // "to" { time: 44700, viseme: 'BMP' }, // "me" { time: 45000, viseme: 'AEI' }, // "in" { time: 45300, viseme: 'QW' }, // "whispers" { time: 45800, viseme: 'CDGKNRSTXYZ' }, // "dire" { time: 46000, viseme: 'closed' }, // "Criminals scatter at my arrival" { time: 47000, viseme: 'CDGKNRSTXYZ' }, // "Criminals" { time: 48000, viseme: 'CDGKNRSTXYZ' }, // "scatter" { time: 49000, viseme: 'AEI' }, // "at" { time: 49300, viseme: 'BMP' }, // "my" { time: 49800, viseme: 'AEI' }, // "arrival" { time: 51000, viseme: 'closed' }, // "The cape becomes my second skin" { time: 53500, viseme: 'TH' }, // "The" { time: 54000, viseme: 'CDGKNRSTXYZ' }, // "cape" { time: 54500, viseme: 'BMP' }, // "becomes" { time: 55000, viseme: 'BMP' }, // "my" { time: 55300, viseme: 'CDGKNRSTXYZ' }, // "second" { time: 55800, viseme: 'CDGKNRSTXYZ' }, // "skin" { time: 56000, viseme: 'closed' }, // "This mask - my true survival" { time: 57000, viseme: 'TH' }, // "This" { time: 57500, viseme: 'BMP' }, // "mask" { time: 58500, viseme: 'BMP' }, // "my" { time: 59000, viseme: 'CDGKNRSTXYZ' }, // "true" { time: 60000, viseme: 'CDGKNRSTXYZ' }, // "survival" { time: 62000, viseme: 'closed' }, // "From the darkness I emerge" { time: 63500, viseme: 'FV' }, // "From" { time: 64000, viseme: 'TH' }, // "the" { time: 64500, viseme: 'CDGKNRSTXYZ' }, // "darkness" { time: 66000, viseme: 'AEI' }, // "I" { time: 66500, viseme: 'EE' }, // "emerge" { time: 69000, viseme: 'closed' }, // "Justice is what I serve" { time: 75000, viseme: 'CHJSH' }, // "Justice" { time: 76000, viseme: 'AEI' }, // "is" { time: 76500, viseme: 'QW' }, // "what" { time: 77000, viseme: 'AEI' }, // "I" { time: 77500, viseme: 'CDGKNRSTXYZ' }, // "serve" { time: 79000, viseme: 'closed' }, // "When evil plagues these streets" { time: 85500, viseme: 'QW' }, // "When" { time: 86000, viseme: 'EE' }, // "evil" { time: 87000, viseme: 'BMP' }, // "plagues" { time: 87500, viseme: 'TH' }, // "these" { time: 88000, viseme: 'CDGKNRSTXYZ' }, // "streets" { time: 90000, viseme: 'closed' }, // "I'll be there" (first) { time: 90500, viseme: 'AEI' }, // "I'll" { time: 91000, viseme: 'BMP' }, // "be" { time: 91300, viseme: 'TH' }, // "there" { time: 92000, viseme: 'closed' }, // "I'll be there" (second) { time: 93500, viseme: 'AEI' }, // "I'll" { time: 94000, viseme: 'BMP' }, // "be" { time: 94300, viseme: 'TH' }, // "there" { time: 94500, viseme: 'closed' }, // "I'm Batman" (first) { time: 96000, viseme: 'AEI' }, // "I'm" { time: 96200, viseme: 'BMP' }, // "Batman" { time: 96800, viseme: 'BMP' }, // "man" { time: 97000, viseme: 'closed' }, // "I'm Batman" (second) { time: 98500, viseme: 'AEI' }, // "I'm" { time: 98700, viseme: 'BMP' }, // "Batman" { time: 99300, viseme: 'BMP' }, // "man" { time: 100000, viseme: 'closed' }, // "I'm Batman" (third) { time: 101500, viseme: 'AEI' }, // "I'm" { time: 101700, viseme: 'BMP' }, // "Batman" { time: 102300, viseme: 'BMP' }, // "man" { time: 102500, viseme: 'closed' }, // "I'm Batman" (fourth) { time: 103000, viseme: 'AEI' }, // "I'm" { time: 103200, viseme: 'BMP' }, // "Batman" { time: 103800, viseme: 'BMP' }, // "Bat" { time: 105000, viseme: 'closed' }, // "A gallery of madness I've faced" { time: 109000, viseme: 'AEI' }, // "A" { time: 109300, viseme: 'CDGKNRSTXYZ' }, // "gallery" { time: 110500, viseme: 'O' }, // "of" { time: 111000, viseme: 'BMP' }, // "madness" { time: 111800, viseme: 'AEI' }, // "I've" { time: 112200, viseme: 'FV' }, // "faced" { time: 112500, viseme: 'closed' }, // "The Joker with his twisted grin" { time: 114000, viseme: 'TH' }, // "The" { time: 114300, viseme: 'CHJSH' }, // "Joker" { time: 115000, viseme: 'QW' }, // "with" { time: 115500, viseme: 'AEI' }, // "his" { time: 115800, viseme: 'CDGKNRSTXYZ' }, // "twisted" { time: 116200, viseme: 'CDGKNRSTXYZ' }, // "grin" { time: 116500, viseme: 'closed' }, // "Riddler's puzzles, Penguin's schemes" { time: 120000, viseme: 'CDGKNRSTXYZ' }, // "Riddler's" { time: 121000, viseme: 'BMP' }, // "puzzles" { time: 121800, viseme: 'BMP' }, // "Penguin's" { time: 122500, viseme: 'CDGKNRSTXYZ' }, // "schemes" { time: 123800, viseme: 'closed' }, // "Two-Face's coin, the battle within" { time: 124000, viseme: 'CDGKNRSTXYZ' }, // "Two-Face's" { time: 125000, viseme: 'CDGKNRSTXYZ' }, // "coin" { time: 125500, viseme: 'TH' }, // "the" { time: 126000, viseme: 'BMP' }, // "battle" { time: 127000, viseme: 'QW' }, // "within" { time: 128500, viseme: 'closed' }, // "My allies few, my burden great" { time: 130000, viseme: 'BMP' }, // "My" { time: 130500, viseme: 'AEI' }, // "allies" { time: 131000, viseme: 'FV' }, // "few" { time: 131500, viseme: 'BMP' }, // "my" { time: 131800, viseme: 'BMP' }, // "burden" { time: 132200, viseme: 'CDGKNRSTXYZ' }, // "great" { time: 132000, viseme: 'closed' }, // "The cave below, my sanctuary" { time: 133000, viseme: 'TH' }, // "The" { time: 133300, viseme: 'CDGKNRSTXYZ' }, // "cave" { time: 134000, viseme: 'BMP' }, // "below" { time: 135000, viseme: 'BMP' }, // "my" { time: 135300, viseme: 'CDGKNRSTXYZ' }, // "sanctuary" { time: 137000, viseme: 'closed' }, // "Alfred's wisdom guides my path" { time: 140500, viseme: 'AEI' }, // "Alfred's" { time: 141500, viseme: 'QW' }, // "wisdom" { time: 142500, viseme: 'CDGKNRSTXYZ' }, // "guides" { time: 143000, viseme: 'BMP' }, // "my" { time: 143500, viseme: 'BMP' }, // "path" { time: 144500, viseme: 'closed' }, // "Through this life so solitary" { time: 145000, viseme: 'TH' }, // "Through" { time: 145500, viseme: 'TH' }, // "this" { time: 146000, viseme: 'L' }, // "life" { time: 147000, viseme: 'CDGKNRSTXYZ' }, // "so" { time: 147500, viseme: 'CDGKNRSTXYZ' }, // "solitary" { time: 150500, viseme: 'closed' }, // "From the darkness I emerge" (second chorus) { time: 151500, viseme: 'FV' }, // "From" { time: 152000, viseme: 'TH' }, // "the" { time: 152500, viseme: 'CDGKNRSTXYZ' }, // "darkness" { time: 154000, viseme: 'AEI' }, // "I" { time: 154500, viseme: 'EE' }, // "emerge" { time: 157000, viseme: 'closed' }, // "Justice is what I serve" (second chorus) { time: 163500, viseme: 'CHJSH' }, // "Justice" { time: 164500, viseme: 'AEI' }, // "is" { time: 165000, viseme: 'QW' }, // "what" { time: 165500, viseme: 'AEI' }, // "I" { time: 166000, viseme: 'CDGKNRSTXYZ' }, // "serve" { time: 167500, viseme: 'closed' }, // "When evil plagues these streets" (second chorus) { time: 173500, viseme: 'QW' }, // "When" { time: 174000, viseme: 'EE' }, // "evil" { time: 175000, viseme: 'BMP' }, // "plagues" { time: 175500, viseme: 'TH' }, // "these" { time: 176000, viseme: 'CDGKNRSTXYZ' }, // "streets" { time: 178000, viseme: 'closed' }, // "I'll be there" (third) { time: 178500, viseme: 'AEI' }, // "I'll" { time: 179000, viseme: 'BMP' }, // "be" { time: 179300, viseme: 'TH' }, // "there" { time: 180000, viseme: 'closed' }, // "I'll be there" (fourth) { time: 181000, viseme: 'AEI' }, // "I'll" { time: 181500, viseme: 'BMP' }, // "be" { time: 181800, viseme: 'TH' }, // "there" { time: 182500, viseme: 'closed' }, // "I'm Batman" (fifth) { time: 184000, viseme: 'AEI' }, // "I'm" { time: 184200, viseme: 'BMP' }, // "Batman" { time: 184800, viseme: 'BMP' }, // "man" { time: 185000, viseme: 'closed' }, // "I'm Batman" (sixth) { time: 186500, viseme: 'AEI' }, // "I'm" { time: 186700, viseme: 'BMP' }, // "Batman" { time: 187300, viseme: 'BMP' }, // "man" { time: 188000, viseme: 'closed' }, // "I'm Batman" (seventh) { time: 189000, viseme: 'AEI' }, // "I'm" { time: 189200, viseme: 'BMP' }, // "Batman" { time: 189800, viseme: 'BMP' }, // "man" { time: 190500, viseme: 'closed' }, // "I'm Bat Man" (eighth) { time: 191500, viseme: 'AEI' }, // "I'm" { time: 191700, viseme: 'BMP' }, // "Bat" { time: 192500, viseme: 'BMP' }, // "Man" { time: 193000, viseme: 'closed' }, // "Na na na na na na na na" (first set) { time: 194000, viseme: 'N' }, // "Na" { time: 194200, viseme: 'N' }, // "na" { time: 194400, viseme: 'N' }, // "na" { time: 194600, viseme: 'N' }, // "na" { time: 194800, viseme: 'N' }, // "na" { time: 195000, viseme: 'N' }, // "na" { time: 195200, viseme: 'N' }, // "na" { time: 195400, viseme: 'N' }, // "na" { time: 195600, viseme: 'N' }, // "Na" { time: 195800, viseme: 'N' }, // "na" { time: 196000, viseme: 'N' }, // "na" { time: 196200, viseme: 'N' }, // "na" { time: 196400, viseme: 'N' }, // "na" { time: 196600, viseme: 'N' }, // "na" { time: 196800, viseme: 'N' }, // "na" { time: 197000, viseme: 'N' }, // "na" { time: 197200, viseme: 'closed' }, // "Batman" { time: 198000, viseme: 'BMP' }, // "Batman" { time: 198500, viseme: 'closed' }, // "Na na na na na na na na" (second set) { time: 199000, viseme: 'N' }, // "Na" { time: 199200, viseme: 'N' }, // "na" { time: 199400, viseme: 'N' }, // "na" { time: 199600, viseme: 'N' }, // "na" { time: 199800, viseme: 'N' }, // "na" { time: 200000, viseme: 'N' }, // "na" { time: 200200, viseme: 'N' }, // "na" { time: 200400, viseme: 'N' }, // "na" { time: 200600, viseme: 'N' }, // "Na" { time: 200800, viseme: 'N' }, // "na" { time: 201000, viseme: 'N' }, // "na" { time: 201200, viseme: 'N' }, // "na" { time: 201400, viseme: 'N' }, // "na" { time: 201600, viseme: 'N' }, // "na" { time: 201800, viseme: 'N' }, // "na" { time: 202000, viseme: 'N' }, // "na" { time: 202200, viseme: 'closed' }, // "Batman!" { time: 203000, viseme: 'BMP' }, // "Batman!" { time: 204500, viseme: 'closed' } ];
User prompt
Update with: var MouthAnimator = Container.expand(function () { var self = Container.call(this); // Load all viseme assets self.visemes = { closed: self.attachAsset('visemeAEI', { anchorX: 0.5, anchorY: 0.5, visible: false }), // Default closed AEI: self.attachAsset('visemeAEI', { anchorX: 0.5, anchorY: 0.5, visible: false }), BMP: self.attachAsset('visemeBMP', { anchorX: 0.5, anchorY: 0.5, visible: false }), CDGKNRSTXYZ: self.attachAsset('visemeCDGKNRSTXYZ', { anchorX: 0.5, anchorY: 0.5, visible: false }), CHJSH: self.attachAsset('visemeCHJSH', { anchorX: 0.5, anchorY: 0.5, visible: false }), EE: self.attachAsset('visemeEE', { anchorX: 0.5, anchorY: 0.5, visible: false }), FV: self.attachAsset('visemeFV', { anchorX: 0.5, anchorY: 0.5, visible: false }), L: self.attachAsset('visemeL', { anchorX: 0.5, anchorY: 0.5, visible: false }), N: self.attachAsset('visemeN', { anchorX: 0.5, anchorY: 0.5, visible: false }), O: self.attachAsset('visemeO', { anchorX: 0.5, anchorY: 0.5, visible: false }), TH: self.attachAsset('visemeTH', { anchorX: 0.5, anchorY: 0.5, visible: false }), U: self.attachAsset('visemeU', { anchorX: 0.5, anchorY: 0.5, visible: false }), QW: self.attachAsset('visimeQW', { anchorX: 0.5, anchorY: 0.5, visible: false }) }; self.currentViseme = 'closed'; self.visemes.closed.visible = true; // Lip-sync timeline based on your Batman song breakdown self.lipSyncTimeline = [ // "From the ashes of tragedy" { time: 5500, viseme: 'FV' }, // "From" { time: 6000, viseme: 'TH' }, // "the" { time: 6200, viseme: 'AEI' }, // "ashes" { time: 6800, viseme: 'O' }, // "of" { time: 7000, viseme: 'CDGKNRSTXYZ' }, // "tragedy" { time: 7500, viseme: 'closed' }, // "A guardian rises" { time: 10000, viseme: 'AEI' }, // "A" { time: 10200, viseme: 'CDGKNRSTXYZ' }, // "guardian" { time: 11500, viseme: 'CDGKNRSTXYZ' }, // "rises" { time: 12500, viseme: 'closed' }, // "Not the hero they deserve" { time: 14000, viseme: 'N' }, // "Not" { time: 14300, viseme: 'TH' }, // "the" { time: 14600, viseme: 'EE' }, // "hero" { time: 15200, viseme: 'TH' }, // "they" { time: 15600, viseme: 'CDGKNRSTXYZ' }, // "deserve" { time: 16000, viseme: 'closed' }, // "But the one they need" { time: 17500, viseme: 'BMP' }, // "But" { time: 17800, viseme: 'TH' }, // "the" { time: 18100, viseme: 'QW' }, // "one" { time: 18500, viseme: 'TH' }, // "they" { time: 18800, viseme: 'N' }, // "need" { time: 19500, viseme: 'closed' }, // "Parents fallen in an alley cold" { time: 22500, viseme: 'BMP' }, // "Parents" { time: 23000, viseme: 'FV' }, // "fallen" { time: 23500, viseme: 'AEI' }, // "in" { time: 23800, viseme: 'AEI' }, // "an" { time: 24000, viseme: 'AEI' }, // "alley" { time: 24500, viseme: 'CDGKNRSTXYZ' }, // "cold" { time: 25000, viseme: 'closed' }, // "A boys heart turned to stone" { time: 25000, viseme: 'AEI' }, // "A" { time: 25300, viseme: 'BMP' }, // "boys" { time: 26000, viseme: 'CDGKNRSTXYZ' }, // "heart" { time: 26800, viseme: 'CDGKNRSTXYZ' }, // "turned" { time: 27500, viseme: 'CDGKNRSTXYZ' }, // "to" { time: 28000, viseme: 'CDGKNRSTXYZ' }, // "stone" { time: 29000, viseme: 'closed' }, // "Fortune built on others' pain" { time: 33000, viseme: 'FV' }, // "Fortune" { time: 33600, viseme: 'BMP' }, // "built" { time: 34000, viseme: 'O' }, // "on" { time: 34300, viseme: 'O' }, // "others'" { time: 34800, viseme: 'BMP' }, // "pain" { time: 35000, viseme: 'closed' }, // "Now I claim the night as my own" { time: 36000, viseme: 'N' }, // "Now" { time: 36300, viseme: 'AEI' }, // "I" { time: 36600, viseme: 'CDGKNRSTXYZ' }, // "claim" { time: 37000, viseme: 'TH' }, // "the" { time: 37300, viseme: 'N' }, // "night" { time: 37800, viseme: 'AEI' }, // "as" { time: 38000, viseme: 'BMP' }, // "my" { time: 38500, viseme: 'O' }, // "own" { time: 40000, viseme: 'closed' }, // Continue with more timeline entries... // "Gotham calls to me in whispers dire" { time: 43500, viseme: 'CDGKNRSTXYZ' }, // "Gotham" { time: 44000, viseme: 'CDGKNRSTXYZ' }, // "calls" { time: 44500, viseme: 'CDGKNRSTXYZ' }, // "to" { time: 44700, viseme: 'BMP' }, // "me" { time: 45000, viseme: 'AEI' }, // "in" { time: 45300, viseme: 'QW' }, // "whispers" { time: 45800, viseme: 'CDGKNRSTXYZ' }, // "dire" { time: 46000, viseme: 'closed' }, // Add rest of the timeline entries following the same pattern... // I'll add key moments but you can easily extend this array // "I'm Batman" repeated sections { time: 96000, viseme: 'AEI' }, // "I'm" { time: 96200, viseme: 'BMP' }, // "Batman" { time: 96800, viseme: 'CDGKNRSTXYZ' }, // "man" { time: 97000, viseme: 'closed' }, { time: 98500, viseme: 'AEI' }, // "I'm" { time: 98700, viseme: 'BMP' }, // "Batman" { time: 99300, viseme: 'CDGKNRSTXYZ' }, // "man" { time: 100000, viseme: 'closed' } ]; self.songStartTime = null; self.isLipSyncActive = false; // Position tracking variables var targetX = GAME_WIDTH / 2; var targetY = GAME_HEIGHT / 2 + 200; var smoothingFactor = 0.18; var prevX = null; var prevY = null; // Scale tracking variables var scaleHistory = new Array(5).fill(1); var scaleIndex = 0; var currentScale = 1; // Rotation variables var targetTilt = 0; var tiltSmoothingFactor = 0.11; var tiltScaleFactor = 0.09; function calculateFaceTilt() { if (facekit.leftEye && facekit.rightEye && facekit.mouthCenter) { var eyeMidX = (facekit.leftEye.x + facekit.rightEye.x) / 2; var eyeMidY = (facekit.leftEye.y + facekit.rightEye.y) / 2; var dx = facekit.mouthCenter.x - eyeMidX; var dy = facekit.mouthCenter.y - eyeMidY; var angle = -(Math.atan2(dx, dy) * (180 / Math.PI)); return Math.max(-15, Math.min(15, angle * 0.15)); } return 0; } self.startLipSync = function() { self.songStartTime = Date.now(); self.isLipSyncActive = true; }; self.setViseme = function(visemeName) { if (self.currentViseme !== visemeName && self.visemes[visemeName]) { // Hide current viseme if (self.visemes[self.currentViseme]) { self.visemes[self.currentViseme].visible = false; } // Show new viseme self.visemes[visemeName].visible = true; self.currentViseme = visemeName; } }; self.updateLipSync = function() { if (!self.isLipSyncActive || !self.songStartTime) return; var currentTime = Date.now() - self.songStartTime; // Find the appropriate viseme for current time var targetViseme = 'closed'; for (var i = 0; i < self.lipSyncTimeline.length; i++) { var entry = self.lipSyncTimeline[i]; if (currentTime >= entry.time) { targetViseme = entry.viseme; } else { break; } } self.setViseme(targetViseme); }; self.update = function () { // Update lip sync self.updateLipSync(); // Smooth position tracking if (facekit.mouthCenter) { targetX = facekit.mouthCenter.x; targetY = facekit.mouthCenter.y; if (prevX === null) { prevX = targetX; prevY = targetY; } var newX = prevX * (1 - smoothingFactor) + targetX * smoothingFactor; var newY = prevY * (1 - smoothingFactor) + targetY * smoothingFactor; self.x = newX; self.y = newY; prevX = newX; prevY = newY; } // Dynamic scale adjustment if (facekit.leftEye && facekit.rightEye) { var eyeDistance = Math.abs(facekit.rightEye.x - facekit.leftEye.x); var newScale = eyeDistance / 250; scaleHistory[scaleIndex] = newScale; scaleIndex = (scaleIndex + 1) % scaleHistory.length; var avgScale = scaleHistory.reduce(function (a, b) { return a + b; }, 0) / scaleHistory.length; var targetScale = Math.max(0.6, Math.min(1.4, avgScale)); currentScale = currentScale * 0.85 + targetScale * 0.15; // Apply scale to all visemes Object.keys(self.visemes).forEach(function(key) { self.visemes[key].scaleX = currentScale; self.visemes[key].scaleY = currentScale; }); } // Rotation tracking if (facekit.leftEye && facekit.rightEye) { targetTilt = calculateFaceTilt() * tiltScaleFactor; targetTilt = Math.max(-15, Math.min(15, targetTilt)); self.rotation += (targetTilt - self.rotation) * tiltSmoothingFactor; } }; return self; }); ``` And update the `handleStartButtonPressed` function to start the lip-sync: ```javascript function handleStartButtonPressed() { if (isGameActive) { return; } isGameActive = true; // Remove start button if (startButtonInstance && startButtonInstance.parent) { startButtonInstance.destroy(); startButtonInstance = null; } // Create and show mask maskInstance = new Mask(); game.addChild(maskInstance); var maskGraphicHeight = maskInstance.maskGraphics.height; var yOffset = -50; var maskTargetX, maskTargetY; if (facekit.leftEye && facekit.rightEye && facekit.leftEye.x !== 0 && facekit.rightEye.x !== 0) { maskTargetX = (facekit.leftEye.x + facekit.rightEye.x) / 2; maskTargetY = (facekit.leftEye.y + facekit.rightEye.y) / 2 - maskGraphicHeight * 0.20 + yOffset; } else { maskTargetX = GAME_WIDTH / 2; maskTargetY = GAME_HEIGHT / 3 + yOffset; } // Create mouth animator mouthAnimatorInstance = new MouthAnimator(); var mouthInitialX = facekit.mouthCenter && facekit.mouthCenter.x !== 0 ? facekit.mouthCenter.x : GAME_WIDTH / 2; var mouthInitialY = facekit.mouthCenter && facekit.mouthCenter.y !== 0 ? facekit.mouthCenter.y : GAME_HEIGHT / 2 + 200; mouthAnimatorInstance.x = mouthInitialX; mouthAnimatorInstance.y = mouthInitialY; game.addChild(mouthAnimatorInstance); // Show mask with callback to start music and lip-sync when animation completes maskInstance.show(maskTargetX, maskTargetY, 1200); // Start music and lip-sync after mask animation completes (1.2 seconds) setTimeout(function() { LK.playMusic('batmansong'); mouthAnimatorInstance.startLipSync(); }, 1200); }
Code edit (1 edits merged)
Please save this source code
User prompt
Use the eyes to track the mask height instead of the nose. ↪💡 Consider importing and using the following plugins: @upit/facekit.v1
Code edit (1 edits merged)
Please save this source code
User prompt
Update with: var Mask = Container.expand(function () { var self = Container.call(this); // Store maskGraphics on self to access its properties like height if needed later self.maskGraphics = self.attachAsset('maskImage', { anchorX: 0.5, anchorY: 0.5 }); self.animationFinished = false; // Flag to track if the intro animation is done // Rotation variables - EXACTLY from working example var targetTilt = 0; var tiltSmoothingFactor = 0.11; var tiltScaleFactor = 0.09; self.show = function (targetX, targetY, duration) { self.x = targetX; self.y = -self.maskGraphics.height / 2 - 50; tween(self, { y: targetY }, { duration: duration, easing: tween.easeOutSine, onFinish: function onFinish() { self.animationFinished = true; } }); }; // Function copied EXACTLY from working DragonHead example function calculateFaceTilt() { if (facekit.leftEye && facekit.rightEye && facekit.mouthCenter) { // Calculate midpoint between eyes var eyeMidX = (facekit.leftEye.x + facekit.rightEye.x) / 2; var eyeMidY = (facekit.leftEye.y + facekit.rightEye.y) / 2; // Calculate angle between eye midpoint and mouth, negated to fix direction var dx = facekit.mouthCenter.x - eyeMidX; var dy = facekit.mouthCenter.y - eyeMidY; var angle = -(Math.atan2(dx, dy) * (180 / Math.PI)); // Reduced angle impact return Math.max(-15, Math.min(15, angle * 0.15)); } return 0; // Default to straight when face points aren't available } // Add update method with rotation - EXACTLY like DragonHead self.update = function () { if (!self.animationFinished) { return; } // Rotation tracking - COPIED EXACTLY from working example if (facekit.leftEye && facekit.rightEye) { targetTilt = calculateFaceTilt() * tiltScaleFactor; // Limit rotation to ±15 degrees - DON'T convert to radians here targetTilt = Math.max(-15, Math.min(15, targetTilt)); self.rotation += (targetTilt - self.rotation) * tiltSmoothingFactor; } }; return self; }); var MouthAnimator = Container.expand(function () { var self = Container.call(this); // Visemes array for easier management self.visemes = [self.attachAsset('mouthVisemeClosed', { anchorX: 0.5, anchorY: 0.5, visible: false }), self.attachAsset('mouthVisemeOpenS', { anchorX: 0.5, anchorY: 0.5, visible: false }), self.attachAsset('mouthVisemeOpenM', { anchorX: 0.5, anchorY: 0.5, visible: false }), self.attachAsset('mouthVisemeOpenL', { anchorX: 0.5, anchorY: 0.5, visible: false })]; self.currentVisemeIndex = 0; if (self.visemes.length > 0) { self.visemes[self.currentVisemeIndex].visible = true; } // Rotation variables - EXACTLY from working example var targetTilt = 0; var tiltSmoothingFactor = 0.11; var tiltScaleFactor = 0.09; // Function copied EXACTLY from working DragonHead example function calculateFaceTilt() { if (facekit.leftEye && facekit.rightEye && facekit.mouthCenter) { // Calculate midpoint between eyes var eyeMidX = (facekit.leftEye.x + facekit.rightEye.x) / 2; var eyeMidY = (facekit.leftEye.y + facekit.rightEye.y) / 2; // Calculate angle between eye midpoint and mouth, negated to fix direction var dx = facekit.mouthCenter.x - eyeMidX; var dy = facekit.mouthCenter.y - eyeMidY; var angle = -(Math.atan2(dx, dy) * (180 / Math.PI)); // Reduced angle impact return Math.max(-15, Math.min(15, angle * 0.15)); } return 0; // Default to straight when face points aren't available } self.updateViseme = function (volume) { if (self.visemes.length === 0) { return; } var newIndex = 0; // Default to closed mouth if (volume > 0.6) { // Loud newIndex = 3; // mouthVisemeOpenL } else if (volume > 0.3) { // Moderate newIndex = 2; // mouthVisemeOpenM } else if (volume > 0.05) { // Quiet sound, slightly open newIndex = 1; // mouthVisemeOpenS } // else, newIndex remains 0 for mouthVisemeClosed if (self.currentVisemeIndex !== newIndex) { self.visemes[self.currentVisemeIndex].visible = false; // Ensure newIndex is valid before accessing if (newIndex >= 0 && newIndex < self.visemes.length) { self.visemes[newIndex].visible = true; self.currentVisemeIndex = newIndex; } } }; // Add update method with rotation - EXACTLY like DragonHead self.update = function () { // Rotation tracking - COPIED EXACTLY from working example if (facekit.leftEye && facekit.rightEye) { targetTilt = calculateFaceTilt() * tiltScaleFactor; // Limit rotation to ±15 degrees - DON'T convert to radians here targetTilt = Math.max(-15, Math.min(15, targetTilt)); self.rotation += (targetTilt - self.rotation) * tiltSmoothingFactor; } }; return self; }); ``` And update your game loop to call the `update` methods: ```javascript // Game update loop game.update = function () { if (isGameActive) { // Update mouth animator position and viseme if (mouthAnimatorInstance) { if (facekit.mouthCenter && (facekit.mouthCenter.x !== 0 || facekit.mouthCenter.y !== 0)) { mouthAnimatorInstance.x = facekit.mouthCenter.x; mouthAnimatorInstance.y = facekit.mouthCenter.y; } else { // Fallback if face data is lost mid-game, keep it centered-ish mouthAnimatorInstance.x = GAME_WIDTH / 2; mouthAnimatorInstance.y = GAME_HEIGHT / 2 + 200; } mouthAnimatorInstance.updateViseme(facekit.volume); mouthAnimatorInstance.update(); // Call the update method for rotation } // Update mask position based on face tracking if (maskInstance) { // ... existing position code ... maskInstance.update(); // Call the update method for rotation } } };
User prompt
Update with: var Mask = Container.expand(function () { var self = Container.call(this); // Store maskGraphics on self to access its properties like height if needed later self.maskGraphics = self.attachAsset('maskImage', { anchorX: 0.5, anchorY: 0.5 }); self.animationFinished = false; // Flag to track if the intro animation is done // Rotation variables - copied exactly from working example var targetTilt = 0; var tiltSmoothingFactor = 0.11; var tiltScaleFactor = 0.09; // Function copied exactly from working DragonHead example function calculateFaceTilt() { if (facekit.leftEye && facekit.rightEye && facekit.mouthCenter) { // Calculate midpoint between eyes var eyeMidX = (facekit.leftEye.x + facekit.rightEye.x) / 2; var eyeMidY = (facekit.leftEye.y + facekit.rightEye.y) / 2; // Calculate angle between eye midpoint and mouth, negated to fix direction var dx = facekit.mouthCenter.x - eyeMidX; var dy = facekit.mouthCenter.y - eyeMidY; var angle = -(Math.atan2(dx, dy) * (180 / Math.PI)); // Reduced angle impact return Math.max(-15, Math.min(15, angle * 0.15)); } return 0; // Default to straight when face points aren't available } self.show = function (targetX, targetY, duration) { self.x = targetX; // Start off-screen (above the visible area) // Considering anchorY is 0.5, initial Y should be -half height relative to screen top self.y = -self.maskGraphics.height / 2 - 50; // 50 pixels buffer to be surely off-screen tween(self, { y: targetY }, { duration: duration, easing: tween.easeOutSine, // A smooth easing function onFinish: function onFinish() { // Optional: anything to do after mask finishes animating self.animationFinished = true; // Mark animation as finished } }); }; // Add rotation update method self.updateRotation = function() { if (!self.animationFinished) { return; } // Rotation tracking - copied exactly from working example if (facekit.leftEye && facekit.rightEye) { targetTilt = calculateFaceTilt() * tiltScaleFactor; // Limit rotation to ±15 degrees - DON'T convert to radians here targetTilt = Math.max(-15, Math.min(15, targetTilt)); self.rotation += (targetTilt - self.rotation) * tiltSmoothingFactor; } }; return self; }); var MouthAnimator = Container.expand(function () { var self = Container.call(this); // Visemes array for easier management self.visemes = [self.attachAsset('mouthVisemeClosed', { anchorX: 0.5, anchorY: 0.5, visible: false }), self.attachAsset('mouthVisemeOpenS', { anchorX: 0.5, anchorY: 0.5, visible: false }), self.attachAsset('mouthVisemeOpenM', { anchorX: 0.5, anchorY: 0.5, visible: false }), self.attachAsset('mouthVisemeOpenL', { anchorX: 0.5, anchorY: 0.5, visible: false })]; self.currentVisemeIndex = 0; if (self.visemes.length > 0) { self.visemes[self.currentVisemeIndex].visible = true; } // Rotation variables - copied exactly from working example var targetTilt = 0; var tiltSmoothingFactor = 0.11; var tiltScaleFactor = 0.09; // Function copied exactly from working DragonHead example function calculateFaceTilt() { if (facekit.leftEye && facekit.rightEye && facekit.mouthCenter) { // Calculate midpoint between eyes var eyeMidX = (facekit.leftEye.x + facekit.rightEye.x) / 2; var eyeMidY = (facekit.leftEye.y + facekit.rightEye.y) / 2; // Calculate angle between eye midpoint and mouth, negated to fix direction var dx = facekit.mouthCenter.x - eyeMidX; var dy = facekit.mouthCenter.y - eyeMidY; var angle = -(Math.atan2(dx, dy) * (180 / Math.PI)); // Reduced angle impact return Math.max(-15, Math.min(15, angle * 0.15)); } return 0; // Default to straight when face points aren't available } self.updateViseme = function (volume) { if (self.visemes.length === 0) { return; } var newIndex = 0; // Default to closed mouth if (volume > 0.6) { // Loud newIndex = 3; // mouthVisemeOpenL } else if (volume > 0.3) { // Moderate newIndex = 2; // mouthVisemeOpenM } else if (volume > 0.05) { // Quiet sound, slightly open newIndex = 1; // mouthVisemeOpenS } // else, newIndex remains 0 for mouthVisemeClosed if (self.currentVisemeIndex !== newIndex) { self.visemes[self.currentVisemeIndex].visible = false; // Ensure newIndex is valid before accessing if (newIndex >= 0 && newIndex < self.visemes.length) { self.visemes[newIndex].visible = true; self.currentVisemeIndex = newIndex; } } }; // Add rotation update method self.updateRotation = function() { // Rotation tracking - copied exactly from working example if (facekit.leftEye && facekit.rightEye) { targetTilt = calculateFaceTilt() * tiltScaleFactor; // Limit rotation to ±15 degrees - DON'T convert to radians here targetTilt = Math.max(-15, Math.min(15, targetTilt)); self.rotation += (targetTilt - self.rotation) * tiltSmoothingFactor; } }; return self; }); ``` And update your game loop to call the rotation methods: ```javascript // Game update loop game.update = function () { if (isGameActive) { // Update mouth animator position and viseme if (mouthAnimatorInstance) { if (facekit.mouthCenter && (facekit.mouthCenter.x !== 0 || facekit.mouthCenter.y !== 0)) { mouthAnimatorInstance.x = facekit.mouthCenter.x; mouthAnimatorInstance.y = facekit.mouthCenter.y; } else { // Fallback if face data is lost mid-game, keep it centered-ish mouthAnimatorInstance.x = GAME_WIDTH / 2; mouthAnimatorInstance.y = GAME_HEIGHT / 2 + 200; } mouthAnimatorInstance.updateViseme(facekit.volume); mouthAnimatorInstance.updateRotation(); // Add rotation update } // Update mask position based on face tracking if (maskInstance) { // Ensure maskGraphics is available to get its height // This is safe as maskGraphics is attached in Mask constructor var maskGraphicHeight = maskInstance.maskGraphics.height; // Determine target X for the mask var targetMaskX; if (facekit.noseTip && facekit.noseTip.x !== 0) { targetMaskX = facekit.noseTip.x; } else { // Fallback: Use current X if valid (not 0 or undefined), otherwise center. // This handles cases where noseTip data might be temporarily unavailable or zero. targetMaskX = maskInstance.x !== undefined && maskInstance.x !== 0 ? maskInstance.x : GAME_WIDTH / 2; } maskInstance.x = targetMaskX; // Continuously update X position // For Y position, only update after the initial lowering animation is finished if (maskInstance.animationFinished) { var targetMaskY; var yOffset = -50; // Negative Y offset if (facekit.noseTip && facekit.noseTip.y !== 0) { targetMaskY = facekit.noseTip.y - maskGraphicHeight * 0.20 + yOffset; } else { // Fallback for Y: Use current Y if valid and not the initial off-screen Y, otherwise a default. // The initialOffScreenYPos is the value set in Mask.show when animation starts. var initialOffScreenYPos = -maskGraphicHeight / 2 - 50; // This is the original start position, not the target // Apply offset to fallback as well, ensuring consistency targetMaskY = maskInstance.y !== undefined && maskInstance.y !== initialOffScreenYPos + yOffset ? maskInstance.y : GAME_HEIGHT / 3 + yOffset; } maskInstance.y = targetMaskY; } maskInstance.updateRotation(); // Add rotation update // Else (during animation, i.e., !maskInstance.animationFinished): // Y position is controlled by the tween started in maskInstance.show(). // X position is updated live (as done above), making it follow the face horizontally during the drop. } } }; ↪💡 Consider importing and using the following plugins: @upit/facekit.v1, @upit/tween.v1
Code edit (1 edits merged)
Please save this source code
User prompt
update with: /**** * Assets ****/ LK.init.shape('mouthVisemeClosed', {width:250, height:120, color:0xaf1bdb, shape:'box'}) LK.init.shape('mouthVisemeOpenL', {width:250, height:180, color:0x7607f6, shape:'box'}) LK.init.shape('mouthVisemeOpenM', {width:250, height:160, color:0x90bfa0, shape:'box'}) LK.init.shape('mouthVisemeOpenS', {width:250, height:140, color:0xa2610d, shape:'box'}) LK.init.shape('startButtonImage', {width:400, height:150, color:0xadef39, shape:'box'}) LK.init.image('maskImage', {width:1500, height:1500, id:'683e614fee80fa316a84a8f0'}) LK.init.music('karaokeSong') /**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var facekit = LK.import("@upit/facekit.v1"); /**** * Classes ****/ var Mask = Container.expand(function () { var self = Container.call(this); // Store maskGraphics on self to access its properties like height if needed later self.maskGraphics = self.attachAsset('maskImage', { anchorX: 0.5, anchorY: 0.5 }); self.animationFinished = false; // Flag to track if the intro animation is done // Face tracking variables for smooth movement var targetX = GAME_WIDTH / 2; var targetY = GAME_HEIGHT / 3; var smoothingFactor = 0.12; var prevX = null; var prevY = null; // Rotation tracking variables var targetTilt = 0; var tiltSmoothingFactor = 0.11; var tiltScaleFactor = 0.05; // Reduced for mask subtlety // Scale tracking variables var scaleHistory = new Array(5).fill(1); // Start with default scale of 1 var scaleIndex = 0; // Function to calculate face tilt (adapted from face tracking example) function calculateFaceTilt() { if (facekit.leftEye && facekit.rightEye && facekit.mouthCenter) { // Calculate midpoint between eyes var eyeMidX = (facekit.leftEye.x + facekit.rightEye.x) / 2; var eyeMidY = (facekit.leftEye.y + facekit.rightEye.y) / 2; // Calculate angle between eye midpoint and mouth, negated to fix direction var dx = facekit.mouthCenter.x - eyeMidX; var dy = facekit.mouthCenter.y - eyeMidY; var angle = -(Math.atan2(dx, dy) * (180 / Math.PI)); // Reduced angle impact for mask return Math.max(-10, Math.min(10, angle * 0.1)); } return 0; // Default to straight when face points aren't available } self.show = function (targetX, targetY, duration) { self.x = targetX; // Start off-screen (above the visible area) // Considering anchorY is 0.5, initial Y should be -half height relative to screen top self.y = -self.maskGraphics.height / 2 - 50; // 50 pixels buffer to be surely off-screen tween(self, { y: targetY }, { duration: duration, easing: tween.easeOutSine, // A smooth easing function onFinish: function onFinish() { // Optional: anything to do after mask finishes animating self.animationFinished = true; // Mark animation as finished } }); }; // New smooth update method for face tracking self.updateFaceTracking = function() { if (!self.animationFinished) { return; // Don't track until animation is finished } // Smooth position tracking if (facekit.noseTip && facekit.noseTip.x !== 0) { targetX = facekit.noseTip.x; var maskGraphicHeight = self.maskGraphics.height; var yOffset = -50; // Keep the existing Y offset targetY = facekit.noseTip.y - maskGraphicHeight * 0.20 + yOffset; // Initialize previous positions if not set if (prevX === null) { prevX = targetX; prevY = targetY; } // Weighted average between previous and target position for smooth movement var newX = prevX * (1 - smoothingFactor) + targetX * smoothingFactor; var newY = prevY * (1 - smoothingFactor) + targetY * smoothingFactor; self.x = newX; self.y = newY; // Update previous positions prevX = newX; prevY = newY; } else { // Fallback positioning if face data is lost if (prevX !== null) { self.x = prevX; self.y = prevY; } } // Rotation tracking if (facekit.leftEye && facekit.rightEye) { targetTilt = calculateFaceTilt() * tiltScaleFactor; // Limit rotation to ±10 degrees for mask targetTilt = Math.max(-10, Math.min(10, targetTilt)); // Convert to radians for rotation var targetTiltRad = targetTilt * (Math.PI / 180); self.rotation += (targetTiltRad - self.rotation) * tiltSmoothingFactor; } // Scale tracking if (facekit.leftEye && facekit.rightEye) { var eyeDistance = Math.abs(facekit.rightEye.x - facekit.leftEye.x); var newScale = eyeDistance / 300; // Adjusted divisor for mask // Update rolling average scaleHistory[scaleIndex] = newScale; scaleIndex = (scaleIndex + 1) % scaleHistory.length; // Calculate average scale var avgScale = scaleHistory.reduce(function (a, b) { return a + b; }, 0) / scaleHistory.length; // Apply with gentle smoothing (limited range for mask) var targetScale = Math.max(0.8, Math.min(1.3, avgScale)); self.maskGraphics.scaleX = self.maskGraphics.scaleX * 0.85 + targetScale * 0.15; self.maskGraphics.scaleY = self.maskGraphics.scaleY * 0.85 + targetScale * 0.15; } }; return self; }); var MouthAnimator = Container.expand(function () { var self = Container.call(this); // Visemes array for easier management self.visemes = [self.attachAsset('mouthVisemeClosed', { anchorX: 0.5, anchorY: 0.5, visible: false }), self.attachAsset('mouthVisemeOpenS', { anchorX: 0.5, anchorY: 0.5, visible: false }), self.attachAsset('mouthVisemeOpenM', { anchorX: 0.5, anchorY: 0.5, visible: false }), self.attachAsset('mouthVisemeOpenL', { anchorX: 0.5, anchorY: 0.5, visible: false })]; self.currentVisemeIndex = 0; if (self.visemes.length > 0) { self.visemes[self.currentVisemeIndex].visible = true; } // Smooth mouth tracking variables (adapted from face tracking example) self.mouthOpenness = 0; // Track mouth state from 0 (closed) to 1 (open) self.mouthSmoothingSpeed = 0.15; // Adjust this value to change transition speed // Position tracking variables for smooth movement var targetX = GAME_WIDTH / 2; var targetY = GAME_HEIGHT / 2 + 200; var positionSmoothingFactor = 0.12; var prevX = null; var prevY = null; // Scale tracking variables var scaleHistory = new Array(5).fill(1); var scaleIndex = 0; self.updateViseme = function (volume) { // Use face tracking for mouth state instead of volume var targetMouthState = 0; if (facekit && facekit.mouthOpen) { targetMouthState = 1; } // Smoothly transition mouth state self.mouthOpenness += (targetMouthState - self.mouthOpenness) * self.mouthSmoothingSpeed; // Convert smooth mouth openness to viseme index (0-3) var newIndex = Math.floor(self.mouthOpenness * 3.99); // Ensures we stay in 0-3 range newIndex = Math.max(0, Math.min(3, newIndex)); // Clamp to valid range // Update viseme visibility if changed if (self.currentVisemeIndex !== newIndex) { self.visemes[self.currentVisemeIndex].visible = false; if (newIndex >= 0 && newIndex < self.visemes.length) { self.visemes[newIndex].visible = true; self.currentVisemeIndex = newIndex; } } }; // New smooth update method for position and scale tracking self.updateFaceTracking = function() { // Smooth position tracking if (facekit.mouthCenter && (facekit.mouthCenter.x !== 0 || facekit.mouthCenter.y !== 0)) { targetX = facekit.mouthCenter.x; targetY = facekit.mouthCenter.y; // Initialize previous positions if not set if (prevX === null) { prevX = targetX; prevY = targetY; } // Weighted average between previous and target position for smooth movement var newX = prevX * (1 - positionSmoothingFactor) + targetX * positionSmoothingFactor; var newY = prevY * (1 - positionSmoothingFactor) + targetY * positionSmoothingFactor; self.x = newX; self.y = newY; // Update previous positions prevX = newX; prevY = newY; } else { // Fallback if face data is lost mid-game if (prevX !== null) { self.x = prevX; self.y = prevY; } else { self.x = GAME_WIDTH / 2; self.y = GAME_HEIGHT / 2 + 200; } } // Scale tracking based on face size if (facekit.leftEye && facekit.rightEye) { var eyeDistance = Math.abs(facekit.rightEye.x - facekit.leftEye.x); var newScale = eyeDistance / 250; // Adjusted divisor for mouth // Update rolling average scaleHistory[scaleIndex] = newScale; scaleIndex = (scaleIndex + 1) % scaleHistory.length; // Calculate average scale var avgScale = scaleHistory.reduce(function (a, b) { return a + b; }, 0) / scaleHistory.length; // Apply with gentle smoothing (limited range) var targetScale = Math.max(0.7, Math.min(1.4, avgScale)); // Apply scale to all visemes for (var i = 0; i < self.visemes.length; i++) { self.visemes[i].scaleX = self.visemes[i].scaleX * 0.85 + targetScale * 0.15; self.visemes[i].scaleY = self.visemes[i].scaleY * 0.85 + targetScale * 0.15; } } // Update viseme based on face tracking (call this after position/scale updates) self.updateViseme(facekit.volume); }; return self; }); var StartButton = Container.expand(function () { var self = Container.call(this); self.buttonGraphics = self.attachAsset('startButtonImage', { anchorX: 0.5, anchorY: 0.5 }); self.down = function () { // This will call the globally defined startGame function // when the button is pressed. if (typeof handleStartButtonPressed === 'function') { handleStartButtonPressed(); } }; return self; }); /**** * Initialize Game ****/ // Facekit provides the camera feed as background, so no explicit backgroundColor needed. var game = new LK.Game({}); /**** * Game Code ****/ // Game state variables var isGameActive = false; var startButtonInstance; var maskInstance; var mouthAnimatorInstance; // Game constants var GAME_WIDTH = 2048; var GAME_HEIGHT = 2732; function handleStartButtonPressed() { if (isGameActive) { return; } // Game already started isGameActive = true; // Remove start button if (startButtonInstance && startButtonInstance.parent) { startButtonInstance.destroy(); startButtonInstance = null; } // Create and show mask maskInstance = new Mask(); game.addChild(maskInstance); // Target X for mask: use noseTip.x if available, otherwise screen center var maskTargetX = facekit.noseTip && facekit.noseTip.x !== 0 ? facekit.noseTip.x : GAME_WIDTH / 2; // Target Y for mask: position it over the upper face. // Anchor is center (0.5,0.5). If mask height is H, noseTip.y - H*0.2 might work. // This positions the center of the mask slightly above the nose tip. var maskGraphicHeight = maskInstance.maskGraphics.height; // actual height of the mask graphic var yOffset = -50; // Negative Y offset var maskTargetY = facekit.noseTip && facekit.noseTip.y !== 0 ? facekit.noseTip.y - maskGraphicHeight * 0.20 + yOffset : GAME_HEIGHT / 3 + yOffset; maskInstance.show(maskTargetX, maskTargetY, 1200); // 1.2 seconds animation duration // Create mouth animator mouthAnimatorInstance = new MouthAnimator(); // Initial position, will be updated continuously in game.update var mouthInitialX = facekit.mouthCenter && facekit.mouthCenter.x !== 0 ? facekit.mouthCenter.x : GAME_WIDTH / 2; var mouthInitialY = facekit.mouthCenter && facekit.mouthCenter.y !== 0 ? facekit.mouthCenter.y : GAME_HEIGHT / 2 + 200; mouthAnimatorInstance.x = mouthInitialX; mouthAnimatorInstance.y = mouthInitialY; game.addChild(mouthAnimatorInstance); // Play music LK.playMusic('karaokeSong'); } // Initial game setup function initializeGameScreen() { isGameActive = false; // Reset game state // Create and position the start button startButtonInstance = new StartButton(); // Center the button horizontally startButtonInstance.x = GAME_WIDTH / 2; // Position it towards the bottom of the screen // Anchor is 0.5, 0.5, so take half of button height into account for precise padding from bottom. var buttonHeight = startButtonInstance.buttonGraphics.height; startButtonInstance.y = GAME_HEIGHT - buttonHeight / 2 - 100; // 100px padding from bottom game.addChild(startButtonInstance); } // Call initial setup initializeGameScreen(); // Game update loop game.update = function () { if (isGameActive) { // Update mouth animator with enhanced face tracking if (mouthAnimatorInstance) { mouthAnimatorInstance.updateFaceTracking(); } // Update mask with enhanced face tracking if (maskInstance) { maskInstance.updateFaceTracking(); } } }; // LK engine handles game pause, game over, etc. // No explicit win/loss conditions in this game's description.
Code edit (2 edits merged)
Please save this source code
User prompt
Dynamically change mask scale depending on size of face.
User prompt
Add a negative Y offset of 50 pixels to the mask tracking.
User prompt
Update the players movement continuously as the mask is lowering to make sure it lands on the players upper face smoothly and doesn’t just warp over.
User prompt
When the button is pressed begin to track the mouth right away, but animate the mask smoothly in to place on the players upper face before starting the full tracking. ↪💡 Consider importing and using the following plugins: @upit/facekit.v1, @upit/tween.v1
Code edit (1 edits merged)
Please save this source code
/**** * Plugins ****/ var tween = LK.import("@upit/tween.v1"); var facekit = LK.import("@upit/facekit.v1"); /**** * Classes ****/ var Mask = Container.expand(function () { var self = Container.call(this); // Store maskGraphics on self to access its properties like height if needed later self.maskGraphics = self.attachAsset('maskImage', { anchorX: 0.5, anchorY: 0.4 }); self.animationFinished = false; // Flag to track if the intro animation is done // Position tracking variables (from dragon example) var targetX = GAME_WIDTH / 2; var targetY = GAME_HEIGHT / 3; var smoothingFactor = 0.25; // Increased for closer tracking, more responsive var prevX = null; var prevY = null; // Scale tracking variables (from dragon example) var scaleHistory = new Array(5).fill(1); // Start with default scale of 1 var scaleIndex = 0; var currentScale = 1; // Rotation variables - EXACTLY from working example var targetTilt = 0; var tiltSmoothingFactor = 0.20; // Increased for closer tracking, more responsive var tiltScaleFactor = 0.09; self.show = function (targetX, targetY, duration) { self.x = targetX; self.y = -self.maskGraphics.height / 2 - 50; tween(self, { y: targetY }, { duration: duration, easing: tween.easeOutSine, onFinish: function onFinish() { self.animationFinished = true; } }); }; // Function copied EXACTLY from working DragonHead example function calculateFaceTilt() { if (facekit.leftEye && facekit.rightEye && facekit.mouthCenter) { // Calculate midpoint between eyes var eyeMidX = (facekit.leftEye.x + facekit.rightEye.x) / 2; var eyeMidY = (facekit.leftEye.y + facekit.rightEye.y) / 2; // Calculate angle between eye midpoint and mouth, negated to fix direction var dx = facekit.mouthCenter.x - eyeMidX; var dy = facekit.mouthCenter.y - eyeMidY; var angle = -(Math.atan2(dx, dy) * (180 / Math.PI)); // Reduced angle impact return Math.max(-15, Math.min(15, angle * 0.15)); } return 0; // Default to straight when face points aren't available } // Enhanced update method with smooth position tracking and dynamic scaling self.update = function () { if (!self.animationFinished) { return; } // Smooth position tracking using nose tip for position if (facekit.noseTip) { targetX = facekit.noseTip.x; // Adjust Y position based on nose tip and mask anchor (0.4 from top) // We want the anchor point (0.5, 0.4) of the maskGraphics to be at the nose tip's y, // then adjusted upwards slightly. // self.maskGraphics.height * self.maskGraphics.anchorY is the distance from top of graphic to anchor. // So, noseTip.y - (self.maskGraphics.height * 0.4) would place anchor at nose. // Then, an additional offset to position it correctly. targetY = facekit.noseTip.y - self.maskGraphics.height * (self.maskGraphics.anchorY - 0.1) - 50; // Adjust Y offset as needed // Initialize previous positions if not set if (prevX === null) { prevX = targetX; prevY = targetY; } // Weighted average between previous and target position for smooth tracking var newX = prevX * (1 - smoothingFactor) + targetX * smoothingFactor; var newY = prevY * (1 - smoothingFactor) + targetY * smoothingFactor; self.x = newX; self.y = newY; // Update previous positions prevX = newX; prevY = newY; } // Dynamic scale adjustment based on face size (from dragon example) if (facekit.leftEye && facekit.rightEye) { var eyeDistance = Math.abs(facekit.rightEye.x - facekit.leftEye.x); var newScale = eyeDistance / 300; // Adjust divisor for mask size // Update rolling average for scale smoothing scaleHistory[scaleIndex] = newScale; scaleIndex = (scaleIndex + 1) % scaleHistory.length; // Calculate average scale var avgScale = scaleHistory.reduce(function (a, b) { return a + b; }, 0) / scaleHistory.length; // Apply with gentle smoothing var targetScale = avgScale; // Removed scale limits currentScale = currentScale * 0.75 + targetScale * 0.25; // Increased responsiveness to target scale // Apply scale to mask self.maskGraphics.scaleX = currentScale; self.maskGraphics.scaleY = currentScale; } // Rotation tracking - COPIED EXACTLY from working example if (facekit.leftEye && facekit.rightEye) { targetTilt = calculateFaceTilt() * tiltScaleFactor; // Limit rotation to ±15 degrees - DON'T convert to radians here targetTilt = Math.max(-15, Math.min(15, targetTilt)); self.rotation += (targetTilt - self.rotation) * tiltSmoothingFactor; } }; return self; }); var MouthAnimator = Container.expand(function () { var self = Container.call(this); // Load all viseme assets (keeping your existing setup) self.visemes = { closed: self.attachAsset('visemeClosed', { anchorX: 0.5, anchorY: 0.5, visible: false }), AEI: self.attachAsset('visemeAEI', { anchorX: 0.5, anchorY: 0.5, visible: false }), BMP: self.attachAsset('visemeBMP', { anchorX: 0.5, anchorY: 0.5, visible: false }), CDGKNRSTXYZ: self.attachAsset('visemeCDGKNRSTXYZ', { anchorX: 0.5, anchorY: 0.5, visible: false }), CHJSH: self.attachAsset('visemeCHJSH', { anchorX: 0.5, anchorY: 0.5, visible: false }), EE: self.attachAsset('visemeEE', { anchorX: 0.5, anchorY: 0.5, visible: false }), FV: self.attachAsset('visemeFV', { anchorX: 0.5, anchorY: 0.5, visible: false }), L: self.attachAsset('visemeL', { anchorX: 0.5, anchorY: 0.5, visible: false }), N: self.attachAsset('visemeN', { anchorX: 0.5, anchorY: 0.5, visible: false }), O: self.attachAsset('visemeO', { anchorX: 0.5, anchorY: 0.5, visible: false }), TH: self.attachAsset('visemeTH', { anchorX: 0.5, anchorY: 0.5, visible: false }), U: self.attachAsset('visemeU', { anchorX: 0.5, anchorY: 0.5, visible: false }), QW: self.attachAsset('visimeQW', { anchorX: 0.5, anchorY: 0.5, visible: false }) }; self.currentViseme = 'closed'; self.visemes.closed.visible = true; // Automated lip sync data (converted from your JSON) self.automatedLipSync = [{ start: 0.00, end: 5.03, value: "X" }, { start: 5.03, end: 5.16, value: "B" }, { start: 5.16, end: 5.23, value: "E" }, { start: 5.23, end: 5.30, value: "F" }, { start: 5.30, end: 5.40, value: "A" }, { start: 5.40, end: 5.54, value: "C" }, { start: 5.54, end: 5.61, value: "B" }, { start: 5.61, end: 5.82, value: "C" }, { start: 5.82, end: 5.96, value: "B" }, { start: 5.96, end: 6.03, value: "C" }, { start: 6.03, end: 6.31, value: "B" }, { start: 6.31, end: 6.52, value: "F" }, { start: 6.52, end: 6.73, value: "E" }, { start: 6.73, end: 7.43, value: "B" }, { start: 7.43, end: 9.85, value: "X" }, { start: 9.85, end: 10.03, value: "E" }, { start: 10.03, end: 10.24, value: "C" }, { start: 10.24, end: 10.38, value: "E" }, { start: 10.38, end: 10.52, value: "F" }, { start: 10.52, end: 10.59, value: "D" }, { start: 10.59, end: 11.15, value: "C" }, { start: 11.15, end: 11.23, value: "A" }, { start: 11.23, end: 11.54, value: "D" }, { start: 11.54, end: 11.62, value: "C" }, { start: 11.62, end: 12.77, value: "B" }, { start: 12.77, end: 13.77, value: "X" }, { start: 13.77, end: 13.83, value: "C" }, { start: 13.83, end: 14.44, value: "F" }, { start: 14.44, end: 14.79, value: "E" }, { start: 14.79, end: 15.01, value: "F" }, { start: 15.01, end: 15.09, value: "A" }, { start: 15.09, end: 15.13, value: "B" }, { start: 15.13, end: 15.38, value: "F" }, { start: 15.38, end: 15.45, value: "B" }, { start: 15.45, end: 16.08, value: "E" }, { start: 16.08, end: 16.29, value: "B" }, { start: 16.29, end: 17.16, value: "X" }, { start: 17.16, end: 17.23, value: "C" }, { start: 17.23, end: 17.29, value: "B" }, { start: 17.29, end: 17.99, value: "H" }, { start: 17.99, end: 19.53, value: "B" }, { start: 19.53, end: 19.86, value: "X" }, { start: 19.86, end: 20.09, value: "B" }, { start: 20.09, end: 22.08, value: "X" }, { start: 22.08, end: 22.42, value: "E" }, { start: 22.42, end: 22.77, value: "B" }, { start: 22.77, end: 22.85, value: "A" }, { start: 22.85, end: 23.02, value: "E" }, { start: 23.02, end: 23.30, value: "B" }, { start: 23.30, end: 23.65, value: "C" }, { start: 23.65, end: 23.86, value: "B" }, { start: 23.86, end: 24.49, value: "F" }, { start: 24.49, end: 24.91, value: "B" }, { start: 24.91, end: 24.98, value: "E" }, { start: 24.98, end: 25.05, value: "A" }, { start: 25.05, end: 25.33, value: "E" }, { start: 25.33, end: 25.75, value: "B" }, { start: 25.75, end: 25.82, value: "D" }, { start: 25.82, end: 25.96, value: "B" }, { start: 25.96, end: 26.17, value: "F" }, { start: 26.17, end: 26.45, value: "E" }, { start: 26.45, end: 27.08, value: "B" }, { start: 27.08, end: 27.43, value: "E" }, { start: 27.43, end: 27.57, value: "F" }, { start: 27.57, end: 27.71, value: "C" }, { start: 27.71, end: 28.48, value: "B" }, { start: 28.48, end: 32.70, value: "X" }, { start: 32.70, end: 32.82, value: "B" }, { start: 32.82, end: 33.03, value: "E" }, { start: 33.03, end: 33.45, value: "B" }, { start: 33.45, end: 33.66, value: "E" }, { start: 33.66, end: 33.73, value: "F" }, { start: 33.73, end: 33.87, value: "B" }, { start: 33.87, end: 34.01, value: "E" }, { start: 34.01, end: 34.29, value: "C" }, { start: 34.29, end: 34.36, value: "B" }, { start: 34.36, end: 34.50, value: "E" }, { start: 34.50, end: 34.71, value: "B" }, { start: 34.71, end: 34.85, value: "E" }, { start: 34.85, end: 35.13, value: "C" }, { start: 35.13, end: 35.20, value: "F" }, { start: 35.20, end: 35.48, value: "B" }, { start: 35.48, end: 35.55, value: "C" }, { start: 35.55, end: 35.76, value: "E" }, { start: 35.76, end: 35.90, value: "F" }, { start: 35.90, end: 36.60, value: "B" }, { start: 36.60, end: 36.71, value: "A" }, { start: 36.71, end: 36.89, value: "D" }, { start: 36.89, end: 36.94, value: "C" }, { start: 36.94, end: 37.47, value: "B" }, { start: 37.47, end: 37.55, value: "A" }, { start: 37.55, end: 37.65, value: "D" }, { start: 37.65, end: 37.72, value: "B" }, { start: 37.72, end: 37.92, value: "F" }, { start: 37.92, end: 38.00, value: "E" }, { start: 38.00, end: 40.10, value: "H" }, { start: 40.10, end: 43.42, value: "X" }, { start: 43.42, end: 43.67, value: "B" }, { start: 43.67, end: 43.74, value: "G" }, { start: 43.74, end: 43.88, value: "F" }, { start: 43.88, end: 44.23, value: "E" }, { start: 44.23, end: 44.44, value: "F" }, { start: 44.44, end: 44.58, value: "B" }, { start: 44.58, end: 44.79, value: "F" }, { start: 44.79, end: 45.07, value: "B" }, { start: 45.07, end: 45.35, value: "F" }, { start: 45.35, end: 45.63, value: "E" }, { start: 45.63, end: 45.70, value: "B" }, { start: 45.70, end: 46.12, value: "E" }, { start: 46.12, end: 46.40, value: "C" }, { start: 46.40, end: 46.45, value: "B" }, { start: 46.45, end: 47.13, value: "F" }, { start: 47.13, end: 47.27, value: "C" }, { start: 47.27, end: 47.34, value: "B" }, { start: 47.34, end: 47.55, value: "E" }, { start: 47.55, end: 47.76, value: "C" }, { start: 47.76, end: 47.83, value: "B" }, { start: 47.83, end: 48.04, value: "D" }, { start: 48.04, end: 48.11, value: "B" }, { start: 48.11, end: 48.81, value: "F" }, { start: 48.81, end: 48.88, value: "C" }, { start: 48.88, end: 48.95, value: "B" }, { start: 48.95, end: 49.02, value: "E" }, { start: 49.02, end: 50.28, value: "B" }, { start: 50.28, end: 50.61, value: "A" }, { start: 50.61, end: 53.74, value: "X" }, { start: 53.74, end: 54.23, value: "B" }, { start: 54.23, end: 54.31, value: "A" }, { start: 54.31, end: 54.51, value: "B" }, { start: 54.51, end: 54.79, value: "C" }, { start: 54.79, end: 54.90, value: "A" }, { start: 54.90, end: 55.01, value: "B" }, { start: 55.01, end: 55.12, value: "A" }, { start: 55.12, end: 55.18, value: "C" }, { start: 55.18, end: 55.52, value: "B" }, { start: 55.52, end: 55.59, value: "C" }, { start: 55.59, end: 55.87, value: "B" }, { start: 55.87, end: 56.22, value: "F" }, { start: 56.22, end: 56.36, value: "B" }, { start: 56.36, end: 56.66, value: "F" }, { start: 56.66, end: 56.74, value: "E" }, { start: 56.74, end: 56.89, value: "H" }, { start: 56.89, end: 57.17, value: "C" }, { start: 57.17, end: 57.69, value: "B" }, { start: 57.69, end: 57.92, value: "C" }, { start: 57.92, end: 58.20, value: "B" }, { start: 58.20, end: 59.04, value: "F" }, { start: 59.04, end: 59.18, value: "B" }, { start: 59.18, end: 59.39, value: "E" }, { start: 59.39, end: 59.53, value: "G" }, { start: 59.53, end: 59.98, value: "D" }, { start: 59.98, end: 60.06, value: "C" }, { start: 60.06, end: 60.37, value: "B" }, { start: 60.37, end: 60.51, value: "G" }, { start: 60.51, end: 60.65, value: "E" }, { start: 60.65, end: 62.19, value: "H" }, { start: 62.19, end: 62.33, value: "X" }, { start: 62.33, end: 62.55, value: "B" }, { start: 62.55, end: 63.24, value: "X" }, { start: 63.24, end: 63.38, value: "B" }, { start: 63.38, end: 63.73, value: "E" }, { start: 63.73, end: 63.87, value: "F" }, { start: 63.87, end: 63.99, value: "A" }, { start: 63.99, end: 64.11, value: "B" }, { start: 64.11, end: 64.46, value: "E" }, { start: 64.46, end: 64.60, value: "F" }, { start: 64.60, end: 64.67, value: "B" }, { start: 64.67, end: 64.88, value: "F" }, { start: 64.88, end: 64.95, value: "C" }, { start: 64.95, end: 65.88, value: "B" }, { start: 65.88, end: 66.43, value: "X" }, { start: 66.43, end: 66.62, value: "D" }, { start: 66.62, end: 66.81, value: "C" }, { start: 66.81, end: 66.86, value: "E" }, { start: 66.86, end: 67.11, value: "F" }, { start: 67.11, end: 68.93, value: "B" }, { start: 68.93, end: 69.14, value: "X" }, { start: 69.14, end: 70.99, value: "B" }, { start: 70.99, end: 71.13, value: "F" }, { start: 71.13, end: 72.63, value: "D" }, { start: 72.63, end: 72.71, value: "C" }, { start: 72.71, end: 73.16, value: "B" }, { start: 73.16, end: 75.30, value: "X" }, { start: 75.30, end: 75.57, value: "B" }, { start: 75.57, end: 75.71, value: "E" }, { start: 75.71, end: 75.99, value: "B" }, { start: 75.99, end: 76.27, value: "C" }, { start: 76.27, end: 76.34, value: "B" }, { start: 76.34, end: 76.83, value: "F" }, { start: 76.83, end: 77.04, value: "E" }, { start: 77.04, end: 77.11, value: "C" }, { start: 77.11, end: 77.18, value: "H" }, { start: 77.18, end: 77.37, value: "D" }, { start: 77.37, end: 77.42, value: "C" }, { start: 77.42, end: 77.81, value: "B" }, { start: 77.81, end: 79.14, value: "F" }, { start: 79.14, end: 79.48, value: "B" }, { start: 79.48, end: 80.18, value: "D" }, { start: 80.18, end: 80.46, value: "C" }, { start: 80.46, end: 83.12, value: "B" }, { start: 83.12, end: 84.24, value: "E" }, { start: 84.24, end: 84.45, value: "B" }, { start: 84.45, end: 85.37, value: "X" }, { start: 85.37, end: 85.55, value: "F" }, { start: 85.55, end: 86.08, value: "B" }, { start: 86.08, end: 86.62, value: "F" }, { start: 86.62, end: 86.67, value: "C" }, { start: 86.67, end: 87.00, value: "E" }, { start: 87.00, end: 87.28, value: "F" }, { start: 87.28, end: 87.42, value: "D" }, { start: 87.42, end: 87.70, value: "C" }, { start: 87.70, end: 88.54, value: "B" }, { start: 88.54, end: 89.42, value: "D" }, { start: 89.42, end: 89.49, value: "C" }, { start: 89.49, end: 89.73, value: "B" }, { start: 89.73, end: 90.46, value: "X" }, { start: 90.46, end: 90.81, value: "E" }, { start: 90.81, end: 91.27, value: "F" }, { start: 91.27, end: 91.34, value: "E" }, { start: 91.34, end: 91.41, value: "F" }, { start: 91.41, end: 92.11, value: "B" }, { start: 92.11, end: 93.07, value: "X" }, { start: 93.07, end: 93.33, value: "C" }, { start: 93.33, end: 93.40, value: "E" }, { start: 93.40, end: 93.78, value: "F" }, { start: 93.78, end: 93.85, value: "B" }, { start: 93.85, end: 94.16, value: "D" }, { start: 94.16, end: 94.24, value: "C" }, { start: 94.24, end: 95.04, value: "B" }, { start: 95.04, end: 95.79, value: "X" }, { start: 95.79, end: 95.89, value: "C" }, { start: 95.89, end: 95.96, value: "B" }, { start: 95.96, end: 96.05, value: "A" }, { start: 96.05, end: 96.31, value: "C" }, { start: 96.31, end: 96.49, value: "B" }, { start: 96.49, end: 97.18, value: "D" }, { start: 97.18, end: 97.26, value: "C" }, { start: 97.26, end: 98.43, value: "X" }, { start: 98.43, end: 98.54, value: "E" }, { start: 98.54, end: 98.61, value: "F" }, { start: 98.61, end: 98.68, value: "C" }, { start: 98.68, end: 98.75, value: "B" }, { start: 98.75, end: 98.82, value: "F" }, { start: 98.82, end: 99.10, value: "C" }, { start: 99.10, end: 99.18, value: "A" }, { start: 99.18, end: 99.64, value: "C" }, { start: 99.64, end: 99.85, value: "B" }, { start: 99.85, end: 101.13, value: "X" }, { start: 101.13, end: 101.26, value: "D" }, { start: 101.26, end: 101.33, value: "B" }, { start: 101.33, end: 101.45, value: "A" }, { start: 101.45, end: 101.86, value: "B" }, { start: 101.86, end: 102.38, value: "D" }, { start: 102.38, end: 102.46, value: "C" }, { start: 102.46, end: 103.19, value: "B" }, { start: 103.19, end: 103.47, value: "H" }, { start: 103.47, end: 104.52, value: "B" }, { start: 104.52, end: 105.15, value: "D" }, { start: 105.15, end: 105.57, value: "C" }, { start: 105.57, end: 108.41, value: "X" }, { start: 108.41, end: 108.56, value: "B" }, { start: 108.56, end: 108.63, value: "D" }, { start: 108.63, end: 108.84, value: "B" }, { start: 108.84, end: 108.98, value: "C" }, { start: 108.98, end: 109.05, value: "E" }, { start: 109.05, end: 109.33, value: "F" }, { start: 109.33, end: 109.68, value: "B" }, { start: 109.68, end: 109.82, value: "F" }, { start: 109.82, end: 109.89, value: "C" }, { start: 109.89, end: 110.03, value: "B" }, { start: 110.03, end: 110.17, value: "C" }, { start: 110.17, end: 110.24, value: "D" }, { start: 110.24, end: 110.52, value: "B" }, { start: 110.52, end: 110.59, value: "C" }, { start: 110.59, end: 111.01, value: "B" }, { start: 111.01, end: 111.08, value: "C" }, { start: 111.08, end: 111.57, value: "B" }, { start: 111.57, end: 111.64, value: "C" }, { start: 111.64, end: 111.99, value: "B" }, { start: 111.99, end: 113.77, value: "X" }, { start: 113.77, end: 114.16, value: "B" }, { start: 114.16, end: 114.65, value: "E" }, { start: 114.65, end: 114.93, value: "F" }, { start: 114.93, end: 115.00, value: "D" }, { start: 115.00, end: 115.24, value: "C" }, { start: 115.24, end: 115.46, value: "B" }, { start: 115.46, end: 115.60, value: "C" }, { start: 115.60, end: 115.74, value: "B" }, { start: 115.74, end: 115.88, value: "C" }, { start: 115.88, end: 116.09, value: "B" }, { start: 116.09, end: 116.17, value: "A" }, { start: 116.17, end: 116.24, value: "E" }, { start: 116.24, end: 116.73, value: "B" }, { start: 116.73, end: 117.57, value: "X" }, { start: 117.57, end: 117.80, value: "B" }, { start: 117.80, end: 118.81, value: "X" }, { start: 118.81, end: 119.28, value: "B" }, { start: 119.28, end: 119.35, value: "F" }, { start: 119.35, end: 119.49, value: "E" }, { start: 119.49, end: 119.65, value: "A" }, { start: 119.65, end: 119.88, value: "H" }, { start: 119.88, end: 119.95, value: "E" }, { start: 119.95, end: 120.16, value: "B" }, { start: 120.16, end: 120.30, value: "E" }, { start: 120.30, end: 120.37, value: "B" }, { start: 120.37, end: 120.65, value: "E" }, { start: 120.65, end: 120.79, value: "F" }, { start: 120.79, end: 120.93, value: "B" }, { start: 120.93, end: 121.00, value: "C" }, { start: 121.00, end: 121.28, value: "B" }, { start: 121.28, end: 121.49, value: "E" }, { start: 121.49, end: 121.56, value: "F" }, { start: 121.56, end: 121.70, value: "B" }, { start: 121.70, end: 121.98, value: "F" }, { start: 121.98, end: 122.19, value: "B" }, { start: 122.19, end: 122.61, value: "C" }, { start: 122.61, end: 123.56, value: "B" }, { start: 123.56, end: 123.77, value: "X" }, { start: 123.77, end: 123.87, value: "B" }, { start: 123.87, end: 124.02, value: "A" }, { start: 124.02, end: 124.16, value: "G" }, { start: 124.16, end: 124.30, value: "C" }, { start: 124.30, end: 124.72, value: "B" }, { start: 124.72, end: 125.00, value: "E" }, { start: 125.00, end: 125.99, value: "B" }, { start: 125.99, end: 126.06, value: "C" }, { start: 126.06, end: 126.34, value: "D" }, { start: 126.34, end: 126.48, value: "B" }, { start: 126.48, end: 126.62, value: "E" }, { start: 126.62, end: 126.69, value: "F" }, { start: 126.69, end: 126.83, value: "H" }, { start: 126.83, end: 128.26, value: "B" }, { start: 128.26, end: 128.43, value: "E" }, { start: 128.43, end: 129.70, value: "X" }, { start: 129.70, end: 129.85, value: "A" }, { start: 129.85, end: 129.96, value: "D" }, { start: 129.96, end: 130.03, value: "B" }, { start: 130.03, end: 130.17, value: "C" }, { start: 130.17, end: 130.38, value: "B" }, { start: 130.38, end: 130.52, value: "D" }, { start: 130.52, end: 130.59, value: "B" }, { start: 130.59, end: 130.87, value: "F" }, { start: 130.87, end: 131.08, value: "B" }, { start: 131.08, end: 131.15, value: "H" }, { start: 131.15, end: 131.29, value: "D" }, { start: 131.29, end: 131.36, value: "C" }, { start: 131.36, end: 131.50, value: "B" }, { start: 131.50, end: 131.81, value: "D" }, { start: 131.81, end: 131.89, value: "E" }, { start: 131.89, end: 132.20, value: "F" }, { start: 132.20, end: 132.48, value: "B" }, { start: 132.48, end: 132.62, value: "X" }, { start: 132.62, end: 132.69, value: "B" }, { start: 132.69, end: 132.90, value: "C" }, { start: 132.90, end: 133.25, value: "B" }, { start: 133.25, end: 133.45, value: "F" }, { start: 133.45, end: 133.53, value: "E" }, { start: 133.53, end: 133.88, value: "H" }, { start: 133.88, end: 134.02, value: "B" }, { start: 134.02, end: 134.14, value: "X" }, { start: 134.14, end: 134.29, value: "B" }, { start: 134.29, end: 134.36, value: "E" }, { start: 134.36, end: 134.50, value: "B" }, { start: 134.50, end: 134.71, value: "E" }, { start: 134.71, end: 134.85, value: "F" }, { start: 134.85, end: 135.06, value: "D" }, { start: 135.06, end: 135.20, value: "E" }, { start: 135.20, end: 135.27, value: "B" }, { start: 135.27, end: 135.69, value: "C" }, { start: 135.69, end: 135.83, value: "B" }, { start: 135.83, end: 136.53, value: "C" }, { start: 136.53, end: 136.67, value: "B" }, { start: 136.67, end: 140.08, value: "X" }, { start: 140.08, end: 140.22, value: "D" }, { start: 140.22, end: 140.29, value: "B" }, { start: 140.29, end: 140.57, value: "E" }, { start: 140.57, end: 141.20, value: "F" }, { start: 141.20, end: 141.41, value: "B" }, { start: 141.41, end: 141.76, value: "E" }, { start: 141.76, end: 142.11, value: "F" }, { start: 142.11, end: 142.18, value: "C" }, { start: 142.18, end: 142.25, value: "E" }, { start: 142.25, end: 142.74, value: "F" }, { start: 142.74, end: 142.93, value: "D" }, { start: 142.93, end: 142.98, value: "C" }, { start: 142.98, end: 143.21, value: "B" }, { start: 143.21, end: 143.97, value: "D" }, { start: 143.97, end: 144.11, value: "B" }, { start: 144.11, end: 144.27, value: "X" }, { start: 144.27, end: 144.40, value: "B" }, { start: 144.40, end: 145.11, value: "X" }, { start: 145.11, end: 145.58, value: "B" }, { start: 145.58, end: 145.72, value: "F" }, { start: 145.72, end: 145.80, value: "A" }, { start: 145.80, end: 145.87, value: "F" }, { start: 145.87, end: 146.18, value: "D" }, { start: 146.18, end: 146.25, value: "C" }, { start: 146.25, end: 146.49, value: "B" }, { start: 146.49, end: 146.56, value: "G" }, { start: 146.56, end: 146.77, value: "B" }, { start: 146.77, end: 146.85, value: "A" }, { start: 146.85, end: 147.26, value: "E" }, { start: 147.26, end: 147.47, value: "B" }, { start: 147.47, end: 147.75, value: "F" }, { start: 147.75, end: 147.82, value: "E" }, { start: 147.82, end: 148.15, value: "D" }, { start: 148.15, end: 148.20, value: "C" }, { start: 148.20, end: 148.38, value: "B" }, { start: 148.38, end: 148.45, value: "F" }, { start: 148.45, end: 150.48, value: "E" }, { start: 150.48, end: 150.90, value: "B" }, { start: 150.90, end: 151.41, value: "X" }, { start: 151.41, end: 151.76, value: "E" }, { start: 151.76, end: 151.90, value: "F" }, { start: 151.90, end: 152.15, value: "A" }, { start: 152.15, end: 152.40, value: "E" }, { start: 152.40, end: 152.54, value: "F" }, { start: 152.54, end: 152.61, value: "B" }, { start: 152.61, end: 152.89, value: "E" }, { start: 152.89, end: 153.13, value: "F" }, { start: 153.13, end: 153.19, value: "C" }, { start: 153.19, end: 153.38, value: "E" }, { start: 153.38, end: 153.45, value: "F" }, { start: 153.45, end: 154.69, value: "X" }, { start: 154.69, end: 154.96, value: "C" }, { start: 154.96, end: 155.16, value: "A" }, { start: 155.16, end: 155.54, value: "D" }, { start: 155.54, end: 155.62, value: "C" }, { start: 155.62, end: 156.49, value: "B" }, { start: 156.49, end: 156.63, value: "E" }, { start: 156.63, end: 157.19, value: "B" }, { start: 157.19, end: 157.47, value: "E" }, { start: 157.47, end: 157.68, value: "C" }, { start: 157.68, end: 158.18, value: "B" }, { start: 158.18, end: 158.22, value: "F" }, { start: 158.22, end: 159.21, value: "B" }, { start: 159.21, end: 159.83, value: "E" }, { start: 159.83, end: 160.25, value: "F" }, { start: 160.25, end: 160.81, value: "B" }, { start: 160.81, end: 163.32, value: "X" }, { start: 163.32, end: 163.41, value: "B" }, { start: 163.41, end: 163.49, value: "A" }, { start: 163.49, end: 163.68, value: "D" }, { start: 163.68, end: 163.73, value: "C" }, { start: 163.73, end: 163.91, value: "B" }, { start: 163.91, end: 164.12, value: "C" }, { start: 164.12, end: 164.71, value: "B" }, { start: 164.71, end: 164.92, value: "E" }, { start: 164.92, end: 165.13, value: "F" }, { start: 165.13, end: 165.48, value: "D" }, { start: 165.48, end: 165.62, value: "C" }, { start: 165.62, end: 165.90, value: "B" }, { start: 165.90, end: 165.97, value: "C" }, { start: 165.97, end: 167.23, value: "A" }, { start: 167.23, end: 167.68, value: "X" }, { start: 167.68, end: 168.61, value: "A" }, { start: 168.61, end: 168.91, value: "B" }, { start: 168.91, end: 168.98, value: "C" }, { start: 168.98, end: 169.40, value: "E" }, { start: 169.40, end: 169.73, value: "F" }, { start: 169.73, end: 172.56, value: "D" }, { start: 172.56, end: 172.64, value: "C" }, { start: 172.64, end: 173.40, value: "X" }, { start: 173.40, end: 173.53, value: "F" }, { start: 173.53, end: 174.56, value: "B" }, { start: 174.56, end: 174.63, value: "F" }, { start: 174.63, end: 174.70, value: "G" }, { start: 174.70, end: 174.98, value: "E" }, { start: 174.98, end: 175.26, value: "H" }, { start: 175.26, end: 175.68, value: "C" }, { start: 175.68, end: 176.10, value: "B" }, { start: 176.10, end: 176.24, value: "C" }, { start: 176.24, end: 176.45, value: "B" }, { start: 176.45, end: 176.52, value: "E" }, { start: 176.52, end: 177.64, value: "B" }, { start: 177.64, end: 177.85, value: "X" }, { start: 177.85, end: 177.97, value: "B" }, { start: 177.97, end: 178.27, value: "X" }, { start: 178.27, end: 178.40, value: "B" }, { start: 178.40, end: 178.54, value: "D" }, { start: 178.54, end: 178.68, value: "B" }, { start: 178.68, end: 179.17, value: "F" }, { start: 179.17, end: 179.94, value: "B" }, { start: 179.94, end: 181.13, value: "X" }, { start: 181.13, end: 181.25, value: "D" }, { start: 181.25, end: 181.39, value: "B" }, { start: 181.39, end: 181.94, value: "F" }, { start: 181.94, end: 182.33, value: "D" }, { start: 182.33, end: 182.41, value: "C" }, { start: 182.41, end: 182.86, value: "B" }, { start: 182.86, end: 182.98, value: "X" }, { start: 182.98, end: 183.17, value: "B" }, { start: 183.17, end: 183.81, value: "X" }, { start: 183.81, end: 183.96, value: "C" }, { start: 183.96, end: 184.18, value: "A" }, { start: 184.18, end: 184.63, value: "D" }, { start: 184.63, end: 184.71, value: "C" }, { start: 184.71, end: 185.44, value: "B" }, { start: 185.44, end: 186.32, value: "X" }, { start: 186.32, end: 186.49, value: "B" }, { start: 186.49, end: 186.56, value: "C" }, { start: 186.56, end: 186.63, value: "B" }, { start: 186.63, end: 186.78, value: "A" }, { start: 186.78, end: 187.00, value: "C" }, { start: 187.00, end: 188.12, value: "B" }, { start: 188.12, end: 189.10, value: "X" }, { start: 189.10, end: 189.33, value: "C" }, { start: 189.33, end: 189.47, value: "B" }, { start: 189.47, end: 189.66, value: "D" }, { start: 189.66, end: 189.71, value: "C" }, { start: 189.71, end: 190.80, value: "B" }, { start: 190.80, end: 191.22, value: "D" }, { start: 191.22, end: 191.46, value: "C" }, { start: 191.46, end: 191.50, value: "H" }, { start: 191.50, end: 191.54, value: "C" }, { start: 191.54, end: 192.52, value: "G" }, { start: 192.52, end: 192.69, value: "A" }, { start: 192.69, end: 192.83, value: "C" }, { start: 192.83, end: 193.25, value: "B" }, { start: 193.25, end: 193.50, value: "G" }, { start: 193.50, end: 193.71, value: "E" }, { start: 193.71, end: 193.82, value: "A" }, { start: 193.82, end: 194.03, value: "D" }, { start: 194.03, end: 194.09, value: "C" }, { start: 194.09, end: 195.53, value: "B" }, { start: 195.53, end: 195.77, value: "X" }, { start: 195.77, end: 195.95, value: "B" }, { start: 195.95, end: 196.65, value: "C" }, { start: 196.65, end: 198.41, value: "B" }, { start: 198.41, end: 198.66, value: "C" }, { start: 198.66, end: 198.80, value: "B" }, { start: 198.80, end: 199.08, value: "C" }, { start: 199.08, end: 199.15, value: "B" }, { start: 199.15, end: 199.43, value: "C" }, { start: 199.43, end: 199.85, value: "B" }, { start: 199.85, end: 199.99, value: "C" }, { start: 199.99, end: 200.20, value: "B" }, { start: 200.20, end: 200.41, value: "G" }, { start: 200.41, end: 201.15, value: "B" }, { start: 201.15, end: 202.25, value: "D" }, { start: 202.25, end: 202.33, value: "C" }, { start: 202.33, end: 203.09, value: "B" }, { start: 203.09, end: 203.19, value: "C" }, { start: 203.19, end: 203.26, value: "E" }, { start: 203.26, end: 203.33, value: "F" }, { start: 203.33, end: 203.89, value: "B" }, { start: 203.89, end: 206.72, value: "X" }]; // Viseme mapping self.visemeMapping = { 'X': 'closed', 'A': 'AEI', 'B': 'BMP', 'C': 'CDGKNRSTXYZ', 'D': 'AEI', 'E': 'EE', 'F': 'FV', 'G': 'CDGKNRSTXYZ', 'H': 'CHJSH' }; self.songStartTime = null; self.isLipSyncActive = false; self.currentLipSyncIndex = 0; // Position tracking variables (keeping your existing setup) var targetX = GAME_WIDTH / 2; var targetY = GAME_HEIGHT / 2 + 200; var smoothingFactor = 0.30; // Increased for closer tracking, more responsive var prevX = null; var prevY = null; // Scale tracking variables (keeping your existing setup) var scaleHistory = new Array(5).fill(1); var scaleIndex = 0; var currentScale = 1; // Rotation variables (keeping your existing setup) var targetTilt = 0; var tiltSmoothingFactor = 0.20; // Increased for closer tracking, more responsive var tiltScaleFactor = 0.09; function calculateFaceTilt() { if (facekit.leftEye && facekit.rightEye && facekit.mouthCenter) { var eyeMidX = (facekit.leftEye.x + facekit.rightEye.x) / 2; var eyeMidY = (facekit.leftEye.y + facekit.rightEye.y) / 2; var dx = facekit.mouthCenter.x - eyeMidX; var dy = facekit.mouthCenter.y - eyeMidY; var angle = -(Math.atan2(dx, dy) * (180 / Math.PI)); return Math.max(-15, Math.min(15, angle * 0.15)); } return 0; } self.startLipSync = function () { self.songStartTime = Date.now(); self.isLipSyncActive = true; self.currentLipSyncIndex = 0; }; self.setViseme = function (visemeName) { if (self.currentViseme !== visemeName && self.visemes[visemeName]) { if (self.visemes[self.currentViseme]) { self.visemes[self.currentViseme].visible = false; } self.visemes[visemeName].visible = true; self.currentViseme = visemeName; } }; self.updateAutomatedLipSync = function () { if (!self.isLipSyncActive || !self.songStartTime || self.automatedLipSync.length === 0) { return; } var currentTimeSeconds = (Date.now() - self.songStartTime) / 1000.0; var targetVisemeKey = 'closed'; // Start searching from currentLipSyncIndex for optimization for (var i = self.currentLipSyncIndex; i < self.automatedLipSync.length; i++) { var cue = self.automatedLipSync[i]; if (currentTimeSeconds >= cue.start && currentTimeSeconds < cue.end) { var automatedCode = cue.value; targetVisemeKey = self.visemeMapping[automatedCode] || 'closed'; self.currentLipSyncIndex = i; break; } if (currentTimeSeconds >= cue.end) { if (i === self.automatedLipSync.length - 1) { var automatedCode = cue.value; targetVisemeKey = self.visemeMapping[automatedCode] || 'closed'; } } else if (currentTimeSeconds < cue.start) { break; } } if (self.currentLipSyncIndex === self.automatedLipSync.length - 1 && currentTimeSeconds >= self.automatedLipSync[self.automatedLipSync.length - 1].end) { targetVisemeKey = 'closed'; } self.setViseme(targetVisemeKey); }; self.update = function () { // Update automated lip sync self.updateAutomatedLipSync(); // Smooth position tracking if (facekit.mouthCenter) { targetX = facekit.mouthCenter.x; targetY = facekit.mouthCenter.y; if (prevX === null) { prevX = targetX; prevY = targetY; } var newX = prevX * (1 - smoothingFactor) + targetX * smoothingFactor; var newY = prevY * (1 - smoothingFactor) + targetY * smoothingFactor; self.x = newX; self.y = newY; prevX = newX; prevY = newY; } // Dynamic scale adjustment if (facekit.leftEye && facekit.rightEye) { var eyeDistance = Math.abs(facekit.rightEye.x - facekit.leftEye.x); var newScale = eyeDistance / 250; scaleHistory[scaleIndex] = newScale; scaleIndex = (scaleIndex + 1) % scaleHistory.length; var avgScale = scaleHistory.reduce(function (a, b) { return a + b; }, 0) / scaleHistory.length; var targetScale = avgScale; // Removed scale limits currentScale = currentScale * 0.75 + targetScale * 0.25; // Increased responsiveness to target scale Object.keys(self.visemes).forEach(function (key) { self.visemes[key].scaleX = currentScale; self.visemes[key].scaleY = currentScale; }); } // Rotation tracking if (facekit.leftEye && facekit.rightEye) { targetTilt = calculateFaceTilt() * tiltScaleFactor; targetTilt = Math.max(-15, Math.min(15, targetTilt)); self.rotation += (targetTilt - self.rotation) * tiltSmoothingFactor; } }; return self; }); var StartButton = Container.expand(function () { var self = Container.call(this); self.buttonGraphics = self.attachAsset('startButtonImage', { anchorX: 0.5, anchorY: 0.5 }); self.down = function () { // This will call the globally defined startGame function // when the button is pressed. if (typeof handleStartButtonPressed === 'function') { handleStartButtonPressed(); } }; return self; }); /**** * Initialize Game ****/ // Facekit provides the camera feed as background, so no explicit backgroundColor needed. var game = new LK.Game({}); /**** * Game Code ****/ // Game state variables var isGameActive = false; var startButtonInstance; var maskInstance; var mouthAnimatorInstance; // Game constants var GAME_WIDTH = 2048; var GAME_HEIGHT = 2732; var AUTOMATED_VISEME_MAPPING = { 'X': 'closed', // Silence/rest position 'A': 'AEI', // Open vowels (father, palm) 'B': 'BMP', // Lip closure (b, m, p) 'C': 'CDGKNRSTXYZ', // Consonants (c, d, g, k, n, r, s, t, x, y, z) 'D': 'AEI', // Mid vowels (day, face) 'E': 'EE', // High front vowels (see, fleece) 'F': 'FV', // Lip-teeth contact (f, v) 'G': 'CDGKNRSTXYZ', // Back consonants (g, ng) 'H': 'CHJSH' // Fricatives (h, ch, j, sh) }; function handleStartButtonPressed() { if (isGameActive) { return; } isGameActive = true; // Remove start button if (startButtonInstance && startButtonInstance.parent) { startButtonInstance.destroy(); startButtonInstance = null; } // Create and show mask maskInstance = new Mask(); game.addChild(maskInstance); var maskGraphicHeight = maskInstance.maskGraphics.height; var yOffset = -50; var maskTargetX, maskTargetY; if (facekit.leftEye && facekit.rightEye && facekit.leftEye.x !== 0 && facekit.rightEye.x !== 0) { maskTargetX = (facekit.leftEye.x + facekit.rightEye.x) / 2; maskTargetY = (facekit.leftEye.y + facekit.rightEye.y) / 2 - maskGraphicHeight * 0.20 + yOffset; } else { maskTargetX = GAME_WIDTH / 2; maskTargetY = GAME_HEIGHT / 3 + yOffset; } // Create mouth animator mouthAnimatorInstance = new MouthAnimator(); var mouthInitialX = facekit.mouthCenter && facekit.mouthCenter.x !== 0 ? facekit.mouthCenter.x : GAME_WIDTH / 2; var mouthInitialY = facekit.mouthCenter && facekit.mouthCenter.y !== 0 ? facekit.mouthCenter.y : GAME_HEIGHT / 2 + 200; mouthAnimatorInstance.x = mouthInitialX; mouthAnimatorInstance.y = mouthInitialY; game.addChild(mouthAnimatorInstance); // Show mask with callback to start music and lip-sync when animation completes maskInstance.show(maskTargetX, maskTargetY, 1200); // Start music and lip-sync after mask animation completes (1.2 seconds) LK.setTimeout(function () { LK.playMusic('batmansong'); if (mouthAnimatorInstance) { // Ensure mouthAnimatorInstance still exists mouthAnimatorInstance.startLipSync(); } }, 1200); } // Initial game setup function initializeGameScreen() { isGameActive = false; // Reset game state // Create and position the start button startButtonInstance = new StartButton(); // Center the button horizontally startButtonInstance.x = GAME_WIDTH / 2; // Position it towards the bottom of the screen var buttonHeight = startButtonInstance.buttonGraphics.height; startButtonInstance.y = GAME_HEIGHT - buttonHeight / 2 - 100; // 100px padding from bottom game.addChild(startButtonInstance); } // Call initial setup initializeGameScreen(); // Game update loop game.update = function () { if (isGameActive) { // Update mouth animator with enhanced tracking if (mouthAnimatorInstance) { // mouthAnimatorInstance.updateViseme(facekit.volume); // Viseme update is now handled by updateLipSync within MouthAnimator's update mouthAnimatorInstance.update(); // This now includes smooth position tracking, scaling, and lip-sync } // Update mask with enhanced tracking if (maskInstance) { maskInstance.update(); // This now includes smooth position tracking and scaling } } };
===================================================================
--- original.js
+++ change.js
@@ -43,21 +43,15 @@
});
};
// Function copied EXACTLY from working DragonHead example
function calculateFaceTilt() {
- if (facekit.nose && facekit.mouthCenter) {
- // Use nose and mouthCenter
- // Calculate angle between nose and mouth, negated to fix direction
- var dx = facekit.mouthCenter.x - facekit.nose.x;
- var dy = facekit.mouthCenter.y - facekit.nose.y;
- // Negate dy because in screen coordinates, y increases downwards.
- // Or adjust atan2 arguments: Math.atan2(mouthCenter.y - nose.y, mouthCenter.x - nose.x) for angle with positive X-axis.
- // The original code used -(Math.atan2(dx, dy)), let's maintain a similar structure.
- // If dx = mouth.x - nose.x and dy = mouth.y - nose.y,
- // atan2(dy, dx) gives angle relative to positive x-axis.
- // atan2(dx, dy) gives angle relative to positive y-axis (pointing down).
- // Original was -(atan2(mouth.x - eyeMid.x, mouth.y - eyeMid.y))
- // Let's keep it consistent:
+ if (facekit.leftEye && facekit.rightEye && facekit.mouthCenter) {
+ // Calculate midpoint between eyes
+ var eyeMidX = (facekit.leftEye.x + facekit.rightEye.x) / 2;
+ var eyeMidY = (facekit.leftEye.y + facekit.rightEye.y) / 2;
+ // Calculate angle between eye midpoint and mouth, negated to fix direction
+ var dx = facekit.mouthCenter.x - eyeMidX;
+ var dy = facekit.mouthCenter.y - eyeMidY;
var angle = -(Math.atan2(dx, dy) * (180 / Math.PI));
// Reduced angle impact
return Math.max(-15, Math.min(15, angle * 0.15));
}
@@ -67,14 +61,18 @@
self.update = function () {
if (!self.animationFinished) {
return;
}
- if (facekit.nose) {
- // Check if nose data is available for positioning
- // Smooth position tracking using nose
- targetX = facekit.nose.x;
- // Position the mask's anchor (anchorY: 0.4) at the nose y-coordinate.
- targetY = facekit.nose.y;
+ // Smooth position tracking using nose tip for position
+ if (facekit.noseTip) {
+ targetX = facekit.noseTip.x;
+ // Adjust Y position based on nose tip and mask anchor (0.4 from top)
+ // We want the anchor point (0.5, 0.4) of the maskGraphics to be at the nose tip's y,
+ // then adjusted upwards slightly.
+ // self.maskGraphics.height * self.maskGraphics.anchorY is the distance from top of graphic to anchor.
+ // So, noseTip.y - (self.maskGraphics.height * 0.4) would place anchor at nose.
+ // Then, an additional offset to position it correctly.
+ targetY = facekit.noseTip.y - self.maskGraphics.height * (self.maskGraphics.anchorY - 0.1) - 50; // Adjust Y offset as needed
// Initialize previous positions if not set
if (prevX === null) {
prevX = targetX;
prevY = targetY;
@@ -86,37 +84,34 @@
self.y = newY;
// Update previous positions
prevX = newX;
prevY = newY;
- if (facekit.mouthCenter) {
- // Check if mouthCenter is also available for scale and rotation
- // Dynamic scale adjustment based on distance between nose and mouth
- var dxScale = facekit.nose.x - facekit.mouthCenter.x;
- var dyScale = facekit.nose.y - facekit.mouthCenter.y;
- var noseMouthDistance = Math.sqrt(dxScale * dxScale + dyScale * dyScale);
- // Adjust divisor (e.g., 150) based on typical nose-mouth distance and desired mask size.
- // A smaller divisor makes the mask larger for the same distance.
- var newScale = noseMouthDistance / 150;
- // Update rolling average for scale smoothing
- scaleHistory[scaleIndex] = newScale;
- scaleIndex = (scaleIndex + 1) % scaleHistory.length;
- // Calculate average scale
- var avgScale = scaleHistory.reduce(function (a, b) {
- return a + b;
- }, 0) / scaleHistory.length;
- // Apply with gentle smoothing
- var targetScale = avgScale; // Scale limits already removed
- currentScale = currentScale * 0.75 + targetScale * 0.25; // Responsiveness already adjusted
- // Apply scale to mask
- self.maskGraphics.scaleX = currentScale;
- self.maskGraphics.scaleY = currentScale;
- // Rotation tracking (calculateFaceTilt now uses nose and mouth)
- targetTilt = calculateFaceTilt() * tiltScaleFactor;
- // Limit rotation to ±15 degrees
- targetTilt = Math.max(-15, Math.min(15, targetTilt));
- self.rotation += (targetTilt - self.rotation) * tiltSmoothingFactor;
- }
}
+ // Dynamic scale adjustment based on face size (from dragon example)
+ if (facekit.leftEye && facekit.rightEye) {
+ var eyeDistance = Math.abs(facekit.rightEye.x - facekit.leftEye.x);
+ var newScale = eyeDistance / 300; // Adjust divisor for mask size
+ // Update rolling average for scale smoothing
+ scaleHistory[scaleIndex] = newScale;
+ scaleIndex = (scaleIndex + 1) % scaleHistory.length;
+ // Calculate average scale
+ var avgScale = scaleHistory.reduce(function (a, b) {
+ return a + b;
+ }, 0) / scaleHistory.length;
+ // Apply with gentle smoothing
+ var targetScale = avgScale; // Removed scale limits
+ currentScale = currentScale * 0.75 + targetScale * 0.25; // Increased responsiveness to target scale
+ // Apply scale to mask
+ self.maskGraphics.scaleX = currentScale;
+ self.maskGraphics.scaleY = currentScale;
+ }
+ // Rotation tracking - COPIED EXACTLY from working example
+ if (facekit.leftEye && facekit.rightEye) {
+ targetTilt = calculateFaceTilt() * tiltScaleFactor;
+ // Limit rotation to ±15 degrees - DON'T convert to radians here
+ targetTilt = Math.max(-15, Math.min(15, targetTilt));
+ self.rotation += (targetTilt - self.rotation) * tiltSmoothingFactor;
+ }
};
return self;
});
var MouthAnimator = Container.expand(function () {
@@ -2571,19 +2566,17 @@
}
// Create and show mask
maskInstance = new Mask();
game.addChild(maskInstance);
- var maskGraphicHeight = maskInstance.maskGraphics.height; // Still useful for default Y
+ var maskGraphicHeight = maskInstance.maskGraphics.height;
+ var yOffset = -50;
var maskTargetX, maskTargetY;
- // Check if nose data is valid and available
- if (facekit.nose && typeof facekit.nose.x === 'number' && typeof facekit.nose.y === 'number') {
- maskTargetX = facekit.nose.x;
- // The mask's anchorY is 0.4. Positioning its y at facekit.nose.y will align this anchor with the nose.
- maskTargetY = facekit.nose.y;
+ if (facekit.leftEye && facekit.rightEye && facekit.leftEye.x !== 0 && facekit.rightEye.x !== 0) {
+ maskTargetX = (facekit.leftEye.x + facekit.rightEye.x) / 2;
+ maskTargetY = (facekit.leftEye.y + facekit.rightEye.y) / 2 - maskGraphicHeight * 0.20 + yOffset;
} else {
- // Default position if nose isn't detected initially
maskTargetX = GAME_WIDTH / 2;
- maskTargetY = GAME_HEIGHT / 3; // Original default was GAME_HEIGHT / 3 - 50. Adjust if needed.
+ maskTargetY = GAME_HEIGHT / 3 + yOffset;
}
// Create mouth animator
mouthAnimatorInstance = new MouthAnimator();
var mouthInitialX = facekit.mouthCenter && facekit.mouthCenter.x !== 0 ? facekit.mouthCenter.x : GAME_WIDTH / 2;