├── .gitattributes ├── .gitignore ├── LICENSE ├── README.md ├── package-lock.json ├── package.json ├── rollup.config.js ├── src ├── components │ ├── avatar-creator-raw.tsx │ ├── avatar-creator.tsx │ └── index.ts ├── events │ ├── asset-unlocked.event.ts │ ├── avatar-exported.event.ts │ ├── index.ts │ ├── user-authorized.event.ts │ └── user-set.event.ts ├── hooks │ └── use-avatar-creator-url.ts ├── index.tsx ├── types.ts └── utils.ts └── tsconfig.json /.gitattributes: -------------------------------------------------------------------------------- 1 | * text eol=lf 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.log 2 | .DS_Store 3 | node_modules 4 | .cache 5 | dist 6 | storybook-static 7 | .idea/ 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Ready Player Me 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Ready Player Me - React Avatar Creator 2 | 3 | **Ready Player Me - React Avatar Creator** is a set of components and helper methods to help implementing the Ready Player Me Avatar Creator into React projects. 4 | 5 | Codesandbox example for loading the Avatar Creator: https://codesandbox.io/p/sandbox/ready-player-me-react-avatar-creator-qxkqjf 6 | 7 | Codesandbox example for loading the Avatar Creator and visualizing the avatar with the Visage package: https://codesandbox.io/p/sandbox/ready-player-me-visage-example-l4m2k2 8 | 9 | ## Installation 10 | 11 | Ready Player Me React Avatar Creator is available as a [npm package](https://www.npmjs.com/package/@readyplayerme/react-avatar-creator). 12 | 13 | ```bash 14 | npm i @readyplayerme/react-avatar-creator 15 | ``` 16 | 17 | ## Usage 18 | 19 | ```tsx 20 | import { AvatarCreator } from '@readyplayerme/react-avatar-creator'; 21 | 22 | export default function App() { 23 | return ; 24 | } 25 | ``` 26 | 27 | https://user-images.githubusercontent.com/3163281/235168912-a9dacd91-af3a-4b35-81c3-b025e12e333a.mp4 28 | 29 | --- 30 | 31 | # Components 32 | 33 | ## AvatarCreator 34 | 35 | AvatarCreator component helps you load Ready Player Me in an iframe where you can edit your avatar and receive your avatar URL as a post message once your avatar is exported. 36 | 37 | ### Parameters 38 | 39 | **subdomain** _[required]_: string 40 | 41 | - Your Ready Player Me subdomain. You can get one from [Ready Player Me Studio](https://studio.readyplayer.me/). 42 | 43 | **className** _[optional]_: string 44 | 45 | - The css classes to apply to the iframe. 46 | 47 | **style** _[optional]_: CSSProperties 48 | 49 | - The css styles to apply to the iframe. 50 | 51 | **config** _[optional]_: AvatarCreatorConfig 52 | 53 | - Editor Configuration is where you can set url properties of Ready Player Me editor. Read more about these options in [Ready Player Me documentations](https://docs.readyplayer.me/ready-player-me/integration-guides/web-and-native-integration/avatar-creator-integration#configuration-1). 54 | 55 | **onAvatarExported** _[optional]_: (event: AvatarExportedEvent) => void 56 | 57 | - Callback function that is called when avatar is exported. 58 | 59 | **onUserSet** _[optional]_: (event: UserSetEvent) => void 60 | 61 | - Callback function that is called when user id is set. 62 | 63 | **onAssetUnlocked** _[optional]_: (event: AssetUnlockedEvent) => void 64 | 65 | - Callback function that is called when an asset is unlocked. 66 | 67 | **onUserAuthorized** _[optional]_: (event: UserAuthorizedEvent) => void 68 | 69 | - Callback function that is called when the user is authorized. 70 | 71 | ### Example 72 | 73 | ```tsx 74 | import { AvatarCreator, AvatarCreatorConfig, AvatarExportedEvent, UserSetEvent } from '@readyplayerme/react-avatar-creator'; 75 | 76 | const config: AvatarCreatorConfig = { 77 | clearCache: true, 78 | bodyType: 'fullbody', 79 | quickStart: false, 80 | language: 'en', 81 | }; 82 | 83 | const style = { width: '100%', height: '100vh', border: 'none' }; 84 | 85 | export default function App() { 86 | const handleOnUserSet = (event: UserSetEvent) => { 87 | console.log(`User ID is: ${event.data.id}`); 88 | }; 89 | 90 | const handleOnAvatarExported = (event: AvatarExportedEvent) => { 91 | console.log(`Avatar URL is: ${event.data.url}`); 92 | }; 93 | 94 | return ( 95 | <> 96 | 97 | 98 | ); 99 | } 100 | ``` 101 | 102 | ## AvatarCreatorRaw 103 | 104 | AvatarCreatorRaw is a lower level component that gives you everything found in the avatar creator, but without explicit callbacks for each event, so you can have the ability to create your own custom logic around these events, if you choose to do so. 105 | 106 | ### Parameters 107 | 108 | **subdomain** _[required]_: string 109 | 110 | - Your Ready Player Me subdomain. You can get one from [Ready Player Me Studio](https://studio.readyplayer.me/). 111 | 112 | **className** _[optional]_: string 113 | 114 | - The css classes to apply to the iframe. 115 | 116 | **style** _[optional]_: CSSProperties 117 | 118 | - The css styles to apply to the iframe. 119 | 120 | **config** _[optional]_: AvatarCreatorConfig 121 | 122 | - Editor Configuration is where you can set url properties of Ready Player Me editor. Read more about these options in [Ready Player Me documentations](https://docs.readyplayer.me/ready-player-me/integration-guides/web-and-native-integration/avatar-creator-integration#configuration-1). 123 | 124 | **onEventReceived** _[required]_: (event: IFrameEvent) => void 125 | 126 | - Callback function that is called whenever an AvatarCreatorEvent is published 127 | 128 | ### Example 129 | 130 | ```tsx 131 | import { AvatarCreatorConfig, AvatarCreatorEvent, AvatarCreatorRaw } from '@readyplayerme/react-avatar-creator'; 132 | 133 | const config: AvatarCreatorConfig = { 134 | clearCache: true, 135 | bodyType: 'fullbody', 136 | quickStart: false, 137 | language: 'en', 138 | }; 139 | 140 | const style = { width: '100%', height: '100vh', border: 'none' }; 141 | 142 | export default function App() { 143 | const handleCustomEvent = (event: AvatarCreatorEvent) => { 144 | console.log(`Received custom event`, event); 145 | }; 146 | 147 | return ( 148 | <> 149 | 150 | 151 | ); 152 | } 153 | ``` 154 | 155 | ## Using AvatarCreator with Visage 156 | 157 | If you would like to use Visage, with its full capability to edit camera and light properties of the scene and more, you can use AvatarCreator component and Visage components together. 158 | 159 | ```tsx 160 | import { Avatar } from '@readyplayerme/visage'; 161 | import { AvatarCreator, AvatarCreatorConfig } from '@readyplayerme/react-avatar-creator'; 162 | import { useState } from 'react'; 163 | 164 | const subdomain = 'demo'; 165 | 166 | const config: AvatarCreatorConfig = { 167 | clearCache: true, 168 | bodyType: 'fullbody', 169 | quickStart: false, 170 | language: 'en', 171 | }; 172 | 173 | const style = { width: '100%', height: '100vh', border: 'none' }; 174 | 175 | export const YourCustomComponent = () => { 176 | const [url, setUrl] = useState(); 177 | 178 | if (!url) { 179 | return setUrl(event.data.url)} />; 180 | } 181 | return ; 182 | }; 183 | ``` 184 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@readyplayerme/react-avatar-creator", 3 | "version": "0.5.0", 4 | "description": "Ready Player Me - React Avatar Creator", 5 | "keywords": [ 6 | "readyplayerme", 7 | "avatar", 8 | "react", 9 | "typescript", 10 | "glb" 11 | ], 12 | "license": "MIT", 13 | "author": "Ready Player Me", 14 | "main": "dist/index.js", 15 | "module": "dist/index.esm.js", 16 | "typings": "dist/index.d.ts", 17 | "scripts": { 18 | "analyze": "size-limit --why", 19 | "build": "rollup -c", 20 | "lint": "dts lint", 21 | "prepare": "rollup -c", 22 | "size": "size-limit", 23 | "start": "dts watch", 24 | "test": "dts test --passWithNoTests" 25 | }, 26 | "husky": { 27 | "hooks": { 28 | "pre-commit": "dts lint" 29 | } 30 | }, 31 | "prettier": { 32 | "printWidth": 400, 33 | "semi": true, 34 | "singleQuote": true, 35 | "trailingComma": "es5", 36 | "endOfLine": "auto" 37 | }, 38 | "engines": { 39 | "node": ">=12" 40 | }, 41 | "devDependencies": { 42 | "@babel/core": "^7.21.4", 43 | "@rollup/plugin-typescript": "^11.1.2", 44 | "@size-limit/preset-small-lib": "^8.2.4", 45 | "@tsconfig/recommended": "^1.0.2", 46 | "@tsconfig/vite-react": "^1.0.1", 47 | "@types/react": "^17.0.40", 48 | "@types/react-dom": "^17.0.13", 49 | "babel-loader": "^9.1.2", 50 | "dts-cli": "^2.0.2", 51 | "husky": "^8.0.3", 52 | "react": ">=17.0", 53 | "react-dom": ">=17.0", 54 | "react-is": "^18.2.0", 55 | "rollup": "^3.26.0", 56 | "rollup-plugin-dts": "^5.3.0", 57 | "size-limit": "^8.2.4", 58 | "tslib": "^2.5.0", 59 | "typescript": "^5.0.4", 60 | "vite": "^4.3.1" 61 | }, 62 | "peerDependencies": { 63 | "react": ">=16" 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /rollup.config.js: -------------------------------------------------------------------------------- 1 | const typescript = require('@rollup/plugin-typescript'); 2 | const { default: dts } = require('rollup-plugin-dts'); 3 | const packageJson = require('./package.json'); 4 | 5 | // rollup.config.js 6 | module.exports = [ 7 | { 8 | input: 'src/index.tsx', 9 | output: [ 10 | { 11 | file: packageJson.main, 12 | format: 'cjs', 13 | sourcemap: false, 14 | }, 15 | { 16 | file: packageJson.module, 17 | format: 'esm', 18 | sourcemap: false, 19 | }, 20 | ], 21 | plugins: [ 22 | typescript({ 23 | tsconfig: './tsconfig.json', 24 | }), 25 | ], 26 | external: ['react'], 27 | }, 28 | { 29 | input: 'src/index.tsx', 30 | output: [{ file: 'dist/index.d.ts' }], 31 | plugins: [dts()], 32 | }, 33 | ]; 34 | -------------------------------------------------------------------------------- /src/components/avatar-creator-raw.tsx: -------------------------------------------------------------------------------- 1 | import React, { CSSProperties, FC, useEffect, useRef } from 'react'; 2 | import { AvatarCreatorConfig } from '../types'; 3 | import { JSONTryParse } from '../utils'; 4 | import { AvatarCreatorEvent } from '../events'; 5 | import { useAvatarCreatorUrl } from '../hooks/use-avatar-creator-url'; 6 | 7 | const MESSAGE_EVENT = 'message'; 8 | const RPM_TARGET = 'readyplayerme'; 9 | const IFRAME_READY_EVENT = 'v1.frame.ready'; 10 | 11 | export type AvatarCreatorRawProps = { 12 | subdomain: string; 13 | className?: string; 14 | style?: CSSProperties; 15 | config?: AvatarCreatorConfig; 16 | }; 17 | 18 | export type EventReceivedProps = { 19 | onEventReceived?: (event: AvatarCreatorEvent) => void; 20 | }; 21 | 22 | /** 23 | * AvatarCreatorRaw is a React component that allows you to create an avatar using Ready Player Me and receive avatar URL. It exposes the raw events in one callback to allow you to write more custom logic around the event handling. 24 | * @param subdomain The subdomain of your Ready Player Me instance. 25 | * @param className The css classes to apply to this iframe. 26 | * @param style The css styles to apply to this iframe. 27 | * @param avatarCreatorConfig The configuration for the AvatarCreator component. 28 | * @param onEventReceived A callback that is called when an avatar creator event is received. 29 | * @returns A React component. 30 | */ 31 | export const AvatarCreatorRaw: FC = ({ subdomain, className, style, config, onEventReceived }) => { 32 | const frameRef = useRef(null); 33 | const url = useAvatarCreatorUrl(subdomain, config); 34 | 35 | const subscribeToAvatarCreatorEvents = () => { 36 | if (!frameRef.current?.contentWindow) return; 37 | 38 | frameRef.current?.contentWindow?.postMessage( 39 | JSON.stringify({ 40 | target: RPM_TARGET, 41 | type: 'subscribe', 42 | eventName: 'v1.**', 43 | }), 44 | '*' 45 | ); 46 | }; 47 | 48 | const subscribe = (event: MessageEvent) => { 49 | const avatarCreatorEvent = JSONTryParse(event.data); 50 | 51 | if (avatarCreatorEvent?.source !== RPM_TARGET) return; 52 | 53 | if (avatarCreatorEvent?.eventName === IFRAME_READY_EVENT) { 54 | subscribeToAvatarCreatorEvents(); 55 | return; 56 | } 57 | 58 | onEventReceived?.(avatarCreatorEvent); 59 | }; 60 | 61 | useEffect(() => { 62 | window.addEventListener(MESSAGE_EVENT, subscribe); 63 | 64 | return () => { 65 | window.removeEventListener(MESSAGE_EVENT, subscribe); 66 | }; 67 | }, []); 68 | 69 | return