NuwaAI Digital Human PlatformNuwaAI Digital Human Platform
  • 简体中文
  • English
  • 简体中文
  • English
  • Real-time digital-human SDK integration

Real-time digital-human SDK integration

1. Overview

The real-time digital-human SDK provides a simple API for quickly integrating digital-human interactions across platforms, including Web, iOS, Android, and Mini Program.

1.1 Features

  • Multi-platform support: Web, iOS, Android, and Mini Program.
  • Floating-button entry: Open the digital-human chat interface with one click.
  • Custom loading screen: Customize loading UI for a better experience.
  • Hot-word filtering: Filter sensitive terms in interactions.
  • Easy to use: A concise API for quick integration.

1.2 Supported platforms

  • Web (browser)
  • iOS (Swift)
  • Android (Kotlin/Java)
  • Mini Program (WeChat Mini Program)

2. Quick start

2.1 Get an API key

Before you begin, complete these steps on the NuwaAI platform:

  1. Register and sign in to a NuwaAI account.
  2. Record your user ID.
  3. Create a digital human and record its ID.
  4. Create and save an API key.

Get an API key, digital-human ID, and user ID

2.2 Installation SDK

Choose the installation method for your platform:

示例
# Install with npm
npm install nuwaai-human-sdk

# Or download the SDK package directly
# Download URL: https://nuwaai.com/demo/npcTalk/npcTalk.tgz

# Download the SDK package directly
# Download URL: https://docs.nuwaai.com/sdk/NuwaAI-IOSSDK.zip
# Add libNuwaSDK.a and WebRTC.framework as dependencies in Xcode

# Download the Android SDK package
# Download URL: https://docs.nuwaai.com/sdk/nuwaai-human-sdk.aar

# Extract the package and copy the SDK file to the Android project directory
# Set the web-view component's src attribute to the web URL on a WeChat Mini Program page; the page uses WeChat's built-in browser

2.3 First digital-human interaction

Now create your first digital-human interaction application:

示例
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>SDK initialization</title>
     <link rel="stylesheet" href="/node_modules/nuwaai-human-sdk/css/demo.css">
    <script type="module" src="/node_modules/nuwaai-human-sdk/js/npcTalkFloatingButton.js"></script>
</head>
<body>
    <div class="container">
        <h1>🔧 SDK initialization settings</h1>
        <form id="sdkForm">
            <div class="form-group">
                <label for="apiKey">API Key</label>
                <input type="text" id="apiKey" name="apiKey" placeholder="Enter your API key" value="sk-jqZ-gBPcRCG1nLpaenmS1gew7rcCkIs-NUWSPzpd3DeWYxobM_CHm-uPTCa8gDPv">
            </div>
            <div class="form-group">
                <label for="avatarId">Digital-human ID</label>
                <input type="text" id="avatarId" name="avatarId" placeholder="Enter the digital-human ID" value="2027224426241638401">
            </div>
            <div class="form-group">
                <label for="userId">User ID</label>
                <input type="text" id="userId" name="userId" placeholder="Enter the user ID" value="54">
            </div>
            <button type="button" class="btn-init" onclick="initializeSDK()">Initialize SDK</button>
        </form>
        <div id="result" class="result"></div>
    </div>

    <script>
        // Initialize the SDK
        function initializeSDK() {
            let floatingButtonInstance = null;
            const apiKey = document.getElementById('apiKey').value.trim();
            const avatarId = document.getElementById('avatarId').value.trim();
            const userId = document.getElementById('userId').value.trim();
            const resultDiv = document.getElementById('result');
            // Validate the input
            if (!apiKey || !avatarId || !userId) {
                resultDiv.className = 'result error';
                resultDiv.textContent = 'Complete all required fields';
                return;
            }

            fetch('https://api.nuwaai.com/web/apiKey/auth', {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json',
                },
                body: JSON.stringify({
                    secretKey: apiKey,
                }),
            })
            .then(res => res.json())
            .then(data => {
                if (data.code === 0) {
                    console.log('token:', data.data);
                    localStorage.setItem('token', data.data);
                    initWithCustomLoading(avatarId, userId);
                } else {
                    console.error('Request failed:', data.msg);
                }
            })
            .catch(error => {
                console.error('Request error:', error);
            });
            
            function initWithCustomLoading(avatorId, userId) {
                try {
                    // Destroy the previous instance
                    if (floatingButtonInstance) {
                        floatingButtonInstance.destroy();
                    }

                    // Create a new instance
                    // Pass loadingConfig as an object; npcTalkFloatingButton.js handles encoding automatically
                    floatingButtonInstance = new window.NpcTalkFloatingButton({
                        avatorid: avatorId,
                        userid: userId,
                        npcPage1: '/node_modules/nuwaai-human-sdk/index-sdk.html',
                        loadingConfig: null,
                        disabledButton: false, // To hide the button, call floatingButtonInstance.navigateToNpcTalk()
                        title: 'Digital-human chat', // Page title (optional; automatically updates the <title> in index-sdk.html)
                        href: '', // Page logo URL (optional; automatically updates the <link rel="icon"> in index-sdk.html)
                        sliceTime:10000,    // Milliseconds
                        backAddress:'', // Return URL
                        hotWords:['咱们','休息一下','你好呀']   // Hot words used to filter sensitive terms
                    });
                } catch (error) {
                    console.error('Initialization failed:', error);
                }
            }
        }
        
        // Submit when Enter is pressed
        document.getElementById('sdkForm').addEventListener('keypress', function(e) {
            if (e.key === 'Enter') {
                e.preventDefault();
                initializeSDK();
            }
        });
    </script>
</body>
</html>

import UIKit
import NuwaSDK

class ViewController: UIViewController {
    private var apiKeyTextField: UITextField!
    private var avatarIdTextField: UITextField!
    private var userIdTextField: UITextField!
    private var initializeButton: UIButton!
    private var floatingButton: NuwaFloatingButton?
    
    override func viewDidLoad() {
        super.viewDidLoad()
        view.backgroundColor = .white
        title = "Nuwa SDK initialization"
        
        setupUI()
    }
    
    func setupUI() {
        let screenWidth = view.frame.size.width
        let padding: CGFloat = 40
        let fieldWidth = screenWidth - padding * 2
        let fieldHeight: CGFloat = 50
        let verticalSpacing: CGFloat = 24
        let topOffset: CGFloat = 120
        
        // API key input
        let apiKeyLabel = createLabel(withText: "API Key", y: topOffset)
        view.addSubview(apiKeyLabel)
        
        apiKeyTextField = createTextField(withY: apiKeyLabel.frame.origin.y + apiKeyLabel.frame.size.height + 8)
        apiKeyTextField.placeholder = "Enter the API key"
        apiKeyTextField.text = "sk-u96kvX4vRJ9CtX7Zj4KSK-CnHXpihy4B6ehyyw8v8SwfDIUbEwA_HmFTG3E_jGMy"
        view.addSubview(apiKeyTextField)
        
        // Digital-human ID input
        let avatarIdLabel = createLabel(withText: "Digital-human ID", y: apiKeyTextField.frame.origin.y + apiKeyTextField.frame.size.height + verticalSpacing)
        view.addSubview(avatarIdLabel)
        
        avatarIdTextField = createTextField(withY: avatarIdLabel.frame.origin.y + avatarIdLabel.frame.size.height + 8)
        avatarIdTextField.placeholder = "Enter the digital-human ID"
        avatarIdTextField.text = "2031288580266336258"
        view.addSubview(avatarIdTextField)
        
        // User ID input
        let userIdLabel = createLabel(withText: "User ID", y: avatarIdTextField.frame.origin.y + avatarIdTextField.frame.size.height + verticalSpacing)
        view.addSubview(userIdLabel)
        
        userIdTextField = createTextField(withY: userIdLabel.frame.origin.y + userIdLabel.frame.size.height + 8)
        userIdTextField.placeholder = "Enter the user ID"
        userIdTextField.text = "54"
        view.addSubview(userIdTextField)
        
        // Initialize button
        initializeButton = UIButton(type: .system)
        initializeButton.frame = CGRect(x: padding, y: userIdTextField.frame.origin.y + userIdTextField.frame.size.height + verticalSpacing * 2, width: fieldWidth, height: fieldHeight)
        initializeButton.setTitle("Initialize SDK", for: .normal)
        initializeButton.backgroundColor = UIColor(red: 0.122, green: 0.467, blue: 0.918, alpha: 1.0)
        initializeButton.setTitleColor(.white, for: .normal)
        initializeButton.titleLabel?.font = UIFont.systemFont(ofSize: 16, weight: .medium)
        initializeButton.layer.cornerRadius = 8
        initializeButton.layer.shadowColor = UIColor.black.cgColor
        initializeButton.layer.shadowOffset = CGSize(width: 0, height: 2)
        initializeButton.layer.shadowOpacity = 0.1
        initializeButton.layer.shadowRadius = 4
        initializeButton.addTarget(self, action: #selector(initSDK), for: .touchUpInside)
        view.addSubview(initializeButton)
    }
    
    func createLabel(withText text: String, y: CGFloat) -> UILabel {
        let padding: CGFloat = 40
        let screenWidth = view.frame.size.width
        
        let label = UILabel(frame: CGRect(x: padding, y: y, width: screenWidth - padding * 2, height: 24))
        label.text = text
        label.font = UIFont.systemFont(ofSize: 14, weight: .medium)
        label.textColor = UIColor(red: 0.333, green: 0.333, blue: 0.333, alpha: 1.0)
        return label
    }
    
    func createTextField(withY y: CGFloat) -> UITextField {
        let padding: CGFloat = 40
        let screenWidth = view.frame.size.width
        
        let textField = UITextField(frame: CGRect(x: padding, y: y, width: screenWidth - padding * 2, height: 50))
        textField.borderStyle = .roundedRect
        textField.font = UIFont.systemFont(ofSize: 16)
        textField.backgroundColor = UIColor(red: 0.957, green: 0.957, blue: 0.957, alpha: 1.0)
        textField.layer.cornerRadius = 8
        textField.layer.borderWidth = 1
        textField.layer.borderColor = UIColor(red: 0.878, green: 0.878, blue: 0.878, alpha: 1.0).cgColor
        return textField
    }
    
    @objc func initSDK() {
        guard let apiKey = apiKeyTextField.text, !apiKey.isEmpty else {
            showAlert(withTitle: "Notice", message: "Enter the API key")
            return
        }
        
        guard let avatarId = avatarIdTextField.text, !avatarId.isEmpty else {
            showAlert(withTitle: "Notice", message: "Enter the digital-human ID")
            return
        }
        
        let userId = userIdTextField.text ?? ""
        
        // Create the configuration
        let config = NuwaFloatingButtonConfig()
        config.apiKey = apiKey
        config.avatorId = avatarId
        config.userId = userId
        
        // Create and show the floating button
        floatingButton = NuwaFloatingButton(config: config)
        floatingButton?.show(in: view)
        
        // Show a success message
        showAlert(withTitle: "Success", message: "The SDK was initialized and the floating button is now visible")
    }
    
    func showAlert(withTitle title: String, message: String) {
        let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)
        let okAction = UIAlertAction(title: "OK", style: .default, handler: nil)
        alert.addAction(okAction)
        present(alert, animated: true, completion: nil)
    }
}
// Objective-C example
// ViewController.h
#import <UIKit/UIKit.h>
#import "NuwaSDK.h"

@interface ViewController : UIViewController

@property (nonatomic, strong) UITextField *apiKeyTextField;
@property (nonatomic, strong) UITextField *avatarIdTextField;
@property (nonatomic, strong) UITextField *userIdTextField;
@property (nonatomic, strong) UIButton *initializeButton;
@property (nonatomic, strong) NuwaFloatingButton *floatingButton;

@end

// ViewController.m
#import "ViewController.h"

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    self.view.backgroundColor = [UIColor whiteColor];
    self.title = @"Nuwa SDK initialization";
    
    [self setupUI];
}

- (void)setupUI {
    CGFloat screenWidth = self.view.frame.size.width;
    CGFloat padding = 40;
    CGFloat fieldWidth = screenWidth - padding * 2;
    CGFloat fieldHeight = 50;
    CGFloat verticalSpacing = 24;
    CGFloat topOffset = 120;
    
    // API key input
    UILabel *apiKeyLabel = [self createLabelWithText:@"API Key" y:topOffset];
    [self.view addSubview:apiKeyLabel];
    
    self.apiKeyTextField = [self createTextFieldWithY:apiKeyLabel.frame.origin.y + apiKeyLabel.frame.size.height + 8];
    self.apiKeyTextField.placeholder = @"Enter the API key";
    self.apiKeyTextField.text = @"sk-u96kvX4vRJ9CtX7Zj4KSK-CnHXpihy4B6ehyyw8v8SwfDIUbEwA_HmFTG3E_jGMy";
    [self.view addSubview:self.apiKeyTextField];
    
    // Digital-human ID input
    UILabel *avatarIdLabel = [self createLabelWithText:@"Digital-human ID" y:self.apiKeyTextField.frame.origin.y + self.apiKeyTextField.frame.size.height + verticalSpacing];
    [self.view addSubview:avatarIdLabel];
    
    self.avatarIdTextField = [self createTextFieldWithY:avatarIdLabel.frame.origin.y + avatarIdLabel.frame.size.height + 8];
    self.avatarIdTextField.placeholder = @"Enter the digital-human ID";
    self.avatarIdTextField.text = @"2031288580266336258";
    [self.view addSubview:self.avatarIdTextField];
    
    // User ID input
    UILabel *userIdLabel = [self createLabelWithText:@"User ID" y:self.avatarIdTextField.frame.origin.y + self.avatarIdTextField.frame.size.height + verticalSpacing];
    [self.view addSubview:userIdLabel];
    
    self.userIdTextField = [self createTextFieldWithY:userIdLabel.frame.origin.y + userIdLabel.frame.size.height + 8];
    self.userIdTextField.placeholder = @"Enter the user ID";
    self.userIdTextField.text = @"54";
    [self.view addSubview:self.userIdTextField];
    
    // Initialize button
    self.initializeButton = [UIButton buttonWithType:UIButtonTypeSystem];
    self.initializeButton.frame = CGRectMake(padding, self.userIdTextField.frame.origin.y + self.userIdTextField.frame.size.height + verticalSpacing * 2, fieldWidth, fieldHeight);
    [self.initializeButton setTitle:@"Initialize SDK" forState:UIControlStateNormal];
    self.initializeButton.backgroundColor = [UIColor colorWithRed:0.122 green:0.467 blue:0.918 alpha:1.0];
    [self.initializeButton setTitleColor:[UIColor whiteColor] forState:UIControlStateNormal];
    self.initializeButton.titleLabel.font = [UIFont systemFontOfSize:16 weight:UIFontWeightMedium];
    self.initializeButton.layer.cornerRadius = 8;
    self.initializeButton.layer.shadowColor = [UIColor blackColor].CGColor;
    self.initializeButton.layer.shadowOffset = CGSizeMake(0, 2);
    self.initializeButton.layer.shadowOpacity = 0.1;
    self.initializeButton.layer.shadowRadius = 4;
    [self.initializeButton addTarget:self action:@selector(initSDK) forControlEvents:UIControlEventTouchUpInside];
    [self.view addSubview:self.initializeButton];
}

- (UILabel *)createLabelWithText:(NSString *)text y:(CGFloat)y {
    CGFloat padding = 40;
    CGFloat screenWidth = self.view.frame.size.width;
    
    UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(padding, y, screenWidth - padding * 2, 24)];
    label.text = text;
    label.font = [UIFont systemFontOfSize:14 weight:UIFontWeightMedium];
    label.textColor = [UIColor colorWithRed:0.333 green:0.333 blue:0.333 alpha:1.0];
    return label;
}

- (UITextField *)createTextFieldWithY:(CGFloat)y {
    CGFloat padding = 40;
    CGFloat screenWidth = self.view.frame.size.width;
    
    UITextField *textField = [[UITextField alloc] initWithFrame:CGRectMake(padding, y, screenWidth - padding * 2, 50)];
    textField.borderStyle = UITextBorderStyleRoundedRect;
    textField.font = [UIFont systemFontOfSize:16];
    textField.backgroundColor = [UIColor colorWithRed:0.957 green:0.957 blue:0.957 alpha:1.0];
    textField.layer.cornerRadius = 8;
    textField.layer.borderWidth = 1;
    textField.layer.borderColor = [UIColor colorWithRed:0.878 green:0.878 blue:0.878 alpha:1.0].CGColor;
    return textField;
}

- (void)initSDK {
    NSString *apiKey = self.apiKeyTextField.text;
    NSString *avatarId = self.avatarIdTextField.text;
    NSString *userId = self.userIdTextField.text;
    
    if (!apiKey || apiKey.length == 0) {
        [self showAlertWithTitle:@"Notice" message:@"Enter the API key"];
        return;
    }
    
    if (!avatarId || avatarId.length == 0) {
        [self showAlertWithTitle:@"Notice" message:@"Enter the digital-human ID"];
        return;
    }
    
    // Create the configuration
    NuwaFloatingButtonConfig *config = [[NuwaFloatingButtonConfig alloc] init];
    config.apiKey = apiKey;
    config.avatorId = avatarId;
    config.userId = userId;
    
    // Create and show the floating button
    self.floatingButton = [[NuwaFloatingButton alloc] initWithConfig:config];
    [self.floatingButton showInView:self.view];
    
    // Show a success message
    [self showAlertWithTitle:@"Success" message:@"The SDK was initialized and the floating button is now visible"];
}

- (void)showAlertWithTitle:(NSString *)title message:(NSString *)message {
    UIAlertController *alert = [UIAlertController alertControllerWithTitle:title message:message preferredStyle:UIAlertControllerStyleAlert];
    UIAlertAction *okAction = [UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:nil];
    [alert addAction:okAction];
    [self presentViewController:alert animated:YES completion:nil];
}

@end
import android.os.Bundle;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.Toast;

import androidx.appcompat.app.AppCompatActivity;

import com.nuwaai.human.NuwaHumanLoadingConfig;
import com.nuwaai.human.NuwaHumanSDK;
import com.nuwaai.human.NuwaHumanSDKOptions;
import com.nuwaai.sdk.NuwaAIClient;
import com.nuwaai.sdk.NuwaAIConfig;
import com.nuwaai.sdk.NuwaCallback;

import java.util.ArrayList;
import java.util.List;

public class HumanDemoActivity extends AppCompatActivity {

    private EditText etApiKey;
    private EditText etAvatarId;
    private EditText etUserId;
    private EditText etHotWords;
    private CheckBox cbHideFloat;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_human_demo);

        if (getSupportActionBar() != null) {
            getSupportActionBar().setDisplayHomeAsUpEnabled(true);
            getSupportActionBar().setTitle("Digital-human interaction");
        }

        initViews();
        setupClickListeners();
    }

    @Override
    public boolean onSupportNavigateUp() {
        finish();
        return true;
    }

    private void initViews() {
        etApiKey = findViewById(R.id.et_api_key);
        etAvatarId = findViewById(R.id.et_avatar_id);
        etUserId = findViewById(R.id.et_user_id);
        etHotWords = findViewById(R.id.et_hot_words);
        cbHideFloat = findViewById(R.id.cb_hide_float);

        
        etAvatarId.setText("2027224426241638401");
        etUserId.setText("54");
        etHotWords.setText("咱们,休息一下,你好呀");
    }

    private void setupClickListeners() {
        findViewById(R.id.btn_init_sdk).setOnClickListener(v -> initializeSDK());
        findViewById(R.id.btn_navigate_talk).setOnClickListener(v -> {
            NuwaHumanSDK sdk = NuwaHumanSDK.getInstance();
            if (sdk == null) {
                Toast.makeText(this, "Initialize the SDK first", Toast.LENGTH_SHORT).show();
                return;
            }
            sdk.navigateToNpcTalk();
        });
        findViewById(R.id.btn_show_float).setOnClickListener(v -> {
            NuwaHumanSDK sdk = NuwaHumanSDK.getInstance();
            if (sdk == null) {
                Toast.makeText(this, "Initialize the SDK first", Toast.LENGTH_SHORT).show();
                return;
            }
            sdk.showFloatingButton();
        });
        findViewById(R.id.btn_hide_float).setOnClickListener(v -> {
            NuwaHumanSDK sdk = NuwaHumanSDK.getInstance();
            if (sdk == null) {
                Toast.makeText(this, "Initialize the SDK first", Toast.LENGTH_SHORT).show();
                return;
            }
            sdk.hideFloatingButton();
        });
        findViewById(R.id.btn_destroy_sdk).setOnClickListener(v -> {
            NuwaHumanSDK sdk = NuwaHumanSDK.getInstance();
            if (sdk != null) {
                sdk.destroy();
                Toast.makeText(this, "SDK destroyed", Toast.LENGTH_SHORT).show();
            } else {
                Toast.makeText(this, "No SDK instance exists", Toast.LENGTH_SHORT).show();
            }
        });
    }

    private void initializeSDK() {
        String apiKey = etApiKey.getText().toString().trim();
        String avatarId = etAvatarId.getText().toString().trim();
        String userId = etUserId.getText().toString().trim();
        boolean hideFloat = cbHideFloat.isChecked();

        if (avatarId.isEmpty() || userId.isEmpty()) {
            Toast.makeText(this, "Enter the digital-human ID and user ID", Toast.LENGTH_SHORT).show();
            return;
        }
        if (apiKey.isEmpty()) {
            Toast.makeText(this, "Enter the API key (used only for nuwaai-sdk authentication; it is not passed to the digital-human SDK)", Toast.LENGTH_SHORT).show();
            return;
        }

        NuwaAIConfig config = NuwaAIConfig.builder().apiKey(apiKey).build();
        NuwaAIClient.init(config);
        NuwaAIClient.getInstance().authenticate(new NuwaCallback<String>() {
            @Override
            public void onSuccess(String token) {
                runOnUiThread(() -> {
                    NuwaHumanLoadingConfig loadingConfig = NuwaHumanLoadingConfig.builder()
                            .html("<div class=\"loading\">Loading...</div>")
                            .style("display", "flex")
                            .style("flex-direction", "column")
                            .style("align-items", "center")
                            .style("justify-content", "center")
                            .style("background", "#111111")
                            .style("color", "#eeeeee")
                            .additionalCss(
                                    ".custom-loading-container .loading{text-align:center;width:100%;}")
                            .build();

                    NuwaHumanSDKOptions options = NuwaHumanSDKOptions.builder()
                            .avatarId(avatarId)
                            .userId(userId)
                            .token(token)
                            .loadingConfig(loadingConfig)
                            .disabledButton(hideFloat)
                            .hotWords(parseHotWords(etHotWords.getText().toString()))
                            .build();

                    NuwaHumanSDK.initialize(HumanDemoActivity.this, options);
                    Toast.makeText(HumanDemoActivity.this,
                            hideFloat ? "Initialized without showing the floating button" : "Initialized and showing the floating button",
                            Toast.LENGTH_SHORT).show();
                });
            }

            @Override
            public void onFailure(int code, String message) {
                runOnUiThread(() -> Toast.makeText(HumanDemoActivity.this,
                        "Authentication failed: " + message, Toast.LENGTH_SHORT).show());
            }
        });
    }

    private static List<String> parseHotWords(String raw) {
        if (raw == null || raw.trim().isEmpty()) {
            return new ArrayList<>();
        }
        String[] parts = raw.split(",");
        List<String> list = new ArrayList<>();
        for (String p : parts) {
            String t = p.trim();
            if (!t.isEmpty()) {
                list.add(t);
            }
        }
        return list;
    }
}

<!-- pages/webview/webview.wxml: Set the web-view component's src attribute to the web URL on a WeChat Mini Program page; the page uses WeChat's built-in browser. -->
<web-view  src="{{webUrl}}" ></web-view>

You have now integrated the Nuwa real-time digital-human SDK into your application.

Web integration demo: open the demo

3. SDK development details

3.1 Get an access token

Before using the SDK, obtain an access token: Get an access token

3.2 Initialize the SDK

示例
let floatingButtonInstance = new window.NpcTalkFloatingButton({
  avatorid: '2027224426241638401',
  userid: '54',
  npcPage1: 'index-sdk.html',
  loadingConfig: {
    html: '<div class="loading">Loading...</div>',
    style: {
      background: '#fff',
      color: '#333'
    }
  },
  disabledButton: false,
  title: 'Digital-human chat',
  href: '',
  sliceTime: 10000,
  backAddress: '',
  hotWord: ['咱们', '休息一下', '你好呀']
});
// Create the configuration
let config = NuwaFloatingButtonConfig()
config.apiKey = "your_api_key"
config.avatorId = "your_avatar_id"
config.userId = "your_user_id"

// Create and show the floating button
let floatingButton = NuwaFloatingButton(config: config)
floatingButton.show(in: view)
NuwaHumanLoadingConfig loadingConfig = NuwaHumanLoadingConfig.builder()
        .html("<div class=\"loading\">Loading...</div>")
        .additionalCss(
                ".custom-loading-container .loading{text-align:center;width:100%;}")
        .build();

NuwaHumanSDKOptions options = NuwaHumanSDKOptions.builder()
        .avatarId(avatarId)
        .userId(userId)
        .token(token)
        .loadingConfig(loadingConfig)
        .disabledButton(false)
        .build();

NuwaHumanSDK.initialize(HumanDemoActivity.this, options);
// In a Mini Program, set the web-view component to the digital-human web URL
ParameterTypeRequiredDescriptionPlatform
avatoridStringYesDigital-human IDAll
useridStringYesUser IDAll
tokenStringYesAccess tokeniOS/Android/Mini Program
npcPage1StringYesDigital-human chat page pathWeb/Mini Program
loadingConfigObjectNoCustom loading-screen configurationAll
disabledButtonBooleanNoWhether to hide the floating buttonAll
titleStringNoPage titleWeb/iOS/Android
hrefStringNoPage logo URLWeb
sliceTimeNumberNoTime slice in millisecondsWeb
backAddressStringNoReturn URLWeb
hotWordArrayNoHot-word list for sensitive-term filteringAll

3.3 Customize the loading UI

Customize the loading screen to improve user experience:

示例
const loadingConfig = {
  html: `
  <div class="custom-loading-wrapper">
    <div class="custom-loading-spinner"></div>
    <div class="custom-loading-text">Connecting to the digital human...</div>
    <div class="custom-loading-subtext">Please wait</div>
  </div>
`,
  style: {
    background: 'linear-gradient(135deg, #667eea 0%, #764ba2 100%)',
    display: 'flex',
    flexDirection: 'column',
    alignItems: 'center',
    justifyContent: 'center',
    color: 'white',
  },
  additionalCSS: `
    .custom-loading-container .custom-loading-wrapper {
      width: 100%;
      height: 100%;
      display: flex;
      flex-direction: column;
      align-items: center;
      justify-content: center;
    }
    .custom-loading-container .custom-loading-spinner {
      width: 60px;
      height: 60px;
      border: 4px solid rgba(255, 255, 255, 0.3);
      border-top-color: white;
      border-radius: 50%;
      animation: custom-loading-spin 1s linear infinite;
      margin-bottom: 20px;
    }
    @keyframes custom-loading-spin {
      to { transform: rotate(360deg); }
    }
  `,
};

floatingButtonInstance = new window.NpcTalkFloatingButton({
  // Other parameters...
  loadingConfig: loadingConfig
});
// NuwaFloatingButton does not currently support a custom loading screen
// You can display a custom loading state in the UI during initialization

// Example: Show a loading indicator before initialization
func initSDK() {
    // Show the loading indicator
    let activityIndicator = UIActivityIndicatorView(style: .large)
    activityIndicator.center = view.center
    activityIndicator.color = .blue
    view.addSubview(activityIndicator)
    activityIndicator.startAnimating()
    
    // Simulate a network request delay
    DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {
        // Hide the loading indicator
        activityIndicator.stopAnimating()
        activityIndicator.removeFromSuperview()
        
        // Create the configuration and initialize the SDK
        let config = NuwaFloatingButtonConfig()
        config.apiKey = "your_api_key"
        config.avatorId = "your_avatar_id"
        config.userId = "your_user_id"
        
        // Create and show the floating button
        let floatingButton = NuwaFloatingButton(config: config)
        floatingButton.show(in: self.view)
        
        // Show a success message
        self.showAlert(withTitle: "Success", message: "The SDK was initialized and the floating button is now visible")
    }
}
NuwaHumanLoadingConfig loadingConfig = NuwaHumanLoadingConfig.builder()
    .html("<div class=\"loading\">Loading...</div>")
    .style("display", "flex")
    .style("flex-direction", "column")
    .style("align-items", "center")
    .style("justify-content", "center")
    .style("background", "#111111")
    .style("color", "#eeeeee")
    .additionalCss(
            ".custom-loading-container .loading{text-align:center;width:100%;}")
    .build();

NuwaHumanSDKOptions options = NuwaHumanSDKOptions.builder()
    // Other parameters...
    .loadingConfig(loadingConfig)
    .build();    
NuwaHumanSDK.initialize(HumanDemoActivity.this, options);    
// The Mini Program loading screen is controlled by the web page

3.4 Control the floating button

示例
// Hide the floating button
floatingButtonInstance = new window.NpcTalkFloatingButton({
  // Other parameters...
  disabledButton: true
});

// Open the digital-human chat interface manually
floatingButtonInstance.navigateToNpcTalk();

// Destroy the SDK instance
floatingButtonInstance.destroy();
// NuwaFloatingButton provides a simple way to integrate a digital human

// Show the floating button
floatingButton?.show(in: view)

// Hide the floating button
floatingButton?.hide()

// Note: NuwaFloatingButton does not require manual destruction; it releases resources when the view controller is destroyed
// Hide the floating button
NuwaHumanSDKOptions options = NuwaHumanSDKOptions.builder()
  // Other parameters...
  .disabledButton(true)
  .build();

NuwaHumanSDK.initialize(HumanDemoActivity.this, options);

// Open the digital-human interface
NuwaHumanSDK.getInstance().navigateToNpcTalk()

// Show the floating button
NuwaHumanSDK.getInstance().showFloatingButton()

// Hide the floating button
NuwaHumanSDK.getInstance().hideFloatingButton()

// Destroy the SDK instance
NuwaHumanSDK.getInstance().destroy()

// Leaving the page exits the digital-human interface
MethodDescriptionPlatform
showFloatingButton()Show the floating buttonAll
hideFloatingButton()Hide the floating buttonAll
navigateToNpcTalk()Open the digital-human chat interfaceAll

4. Important notes

4.1 Web platform

Requirements

  • The device must have a microphone.
  • Chrome 60+
  • Firefox 55+
  • Safari 12+
  • Edge 79+

Permissions

  • Microphone permission is required.

4.2 iOS platform

Requirements

  • iOS 12.0+
  • Xcode 12.0+
  • Swift 5.0+

Permissions

  1. Add microphone permission to Info.plist:
<key>NSMicrophoneUsageDescription</key>
<string>Microphone access is required for voice calls</string>

Integration steps

  1. Install NuwaSDK:

    • With CocoaPods, add pod 'NuwaSDK' to the Podfile.
    • Or add NuwaSDK.framework directly to the project.
  2. Import the headers:

    import NuwaSDK
    
  3. Create the configuration:

    let config = NuwaFloatingButtonConfig()
    config.apiKey = "your_api_key"
    config.avatorId = "your_avatar_id"
    config.userId = "your_user_id"
    
  4. Create and show the floating button:

    let floatingButton = NuwaFloatingButton(config: config)
    floatingButton.show(in: view)
    
  5. Hide the floating button (optional):

    floatingButton.hide()
    

4.3 Android platform

Requirements

  • Android 5.0+
  • minSdkVersion 21+
  • targetSdkVersion 33+

Permissions

Add the required permissions to AndroidManifest.xml:

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />

4.4 Mini-program platform

Requirements

  • WeChat Mini Program base library 2.10.0 or later.
  • Real-time audio/video capability enabled.

Configuration

Add the required settings to app.json:

{
  "requiredBackgroundModes": ["audio", "video"],
  "permission": {
    "scope.camera": {
      "desc": "Required for video calls"
    },
    "scope.record": {
      "desc": "Required for voice calls"
    }
  }
}

5. FAQ

5.1 Digital-human chat UI does not appear

Issue: The digital-human chat interface does not appear after clicking the floating button.

Solution:

  • Check that the npcPage1 path is correct.
  • Ensure the page exists and is accessible.
  • Check browser console or logs for errors.

5.2 Hot-word filtering does not work

Issue: Hot-word filtering is not taking effect.

Solution:

  • Ensure hotWord is an array.
  • Verify the hot words are configured correctly and use the required format.

5.3 Platform-specific issues

iOS

  • Issue: Floating button does not appear. Solution: Verify the API key and digital-human ID, and check the network.
  • Issue: Video does not appear. Solution: Add camera permission and verify the camera is available.
  • Issue: Audio does not work. Solution: Add microphone permission and ensure device volume is enabled.

Android

  • Issue: Permission denied. Solution: Request microphone permission at runtime.

Mini Program

  • Issue: Unable to open the microphone. Solution: Ensure the user has granted microphone permission.
Last Updated:: 9/18/26, 5:41 PM
Contributors: 邹丹晔, 李伟尧, 005609