React, JavaScript and WebAssembly to Rewrite Native Apps
@FlorianRival
Since a long time
ย open-source, cross-platform game creator designed to be used by everyone
React is perfect for making complex UI
JavaScript is perfect for most applications
GUI (windows, dialogs...)
Filesystem
React.js powered interface
Node.js/browser adapters
X-platform toolkit (wxWidgets)
GDevelop C++ codebase
GDevelop JS codebase
GDevelop C++ codebase
Core classes (game, scene, object, textures...)
Game exporter (JS "transpiler")
libGDevelop.js
git clone https://github.com/emscripten-core/emsdk.git
cd emsdk && ./emsdk install latest && ./emsdk activate latest
./emcc tests/hello_world.c
node a.out.js
For large projects, Emscripten provides replacements that swap GCC or Clang by Emscripten in build system (automake, CMake...)
class ObjectsContainer {
Object& InsertObject(const Object& object, size_t position) { ... };
Object& GetObject(const gd::String& name) { ... };
}
class Layout : public ObjectsContainer {
public:
void setName(const gd::String & name_) { name = name_; };
const gd::String & getName() { return name; };
// ...
}
interface Layout {
void setName([Const] DOMString name);
[Const, Ref] DOMString getName();
// Inherited from ObjectsContainer
[Ref] Object insertObject([Ref] gdObject object, unsigned long pos);
[Ref] Object getObject([Const] DOMString name);
}
"WebIDL" bindings for Emscripten WebIDL binder:
Module().then(gd => {
const layout = new gd.Layout();
layout.setName("My game level");
const object = new gd.Object();
object.setName("My character");
layout.insertObject(object, 0);
console.log(layout.getName()); // "My game level"
console.log(object.getObject("My character")); // Returns a gd.Object
});
Primitive types?
If I pass an object?
Can I debug?
const layout = new gd.Layout();
// ...
gd.destroy(layout); // Binded objects need manual destruction
componentDidMount() { this._project = new gd.Project(); }
componentWillUnmount() { gd.destroy(this._project); }
or use an Effect Hook ๐ฅ
console.log(layout); // { ptr: 6804120 }
Wasm memory
The content of gd.Layout
abort(16). Build with -s ASSERTIONS=1 for more info.
What can go wrong?
GUI (windows, dialogs...)
Filesystem
React.js powered interface
Node.js/browser adapters
X-platform toolkit (wxWidgets)
GDevelop JS codebase
GDevelop C++ codebase
Core classes (game, scene, object, textures...)
Game exporter (JS "transpiler")
libGDevelop.js
const SortableObjectRow = SortableElement(props => (
<ObjectRow ... />
));
const ObjectsList = (props) => (
<List
rowRenderer={({ ... }) => {
<SortableObjectRow />
})
/>
);
const SortableObjectsList =
SortableContainer(ObjectsList);
<SortableObjectsList
onSortEnd={({ oldIndex, newIndex }) => ...}
...
/>
const ObjectRow = () => <div>...</div>;
const ObjectsList = (props) => props.map(group => <ObjectRow ... />)
react-sortable-hoc
react-virtualized
react-mosaic
react-sortable-tree
<svg
onPointerMove={...}
onPointerUp={...}
width={this.props.imageWidth}
height={this.props.imageHeight}
>
<polygon
fill="rgba(255,0,0,0.2)"
stroke="rgba(255,0,0,0.5)"
fileRule="evenodd"
points={vertices.map(vertex =>
`${vertex.getX()},${vertex.getY()}`
).join(' ')}
/>
{vertices.map((vertex, j) => (
<circle
onPointerDown={...}
key={`vertex-${j}`}
fill="rgba(255,0,0,0.75)"
cx={vertex.getX()}
cy={vertex.getY()}
r={5}
/>
))}
</svg>
Measure performance in production (React development build is way slower)
shouldComponentUpdate/memoizationย is often the answer
objects.filter(object => object.getType() !== "") // N wasm calls + string conversion
.map(object => object.getVariables(project)); // N wasm calls + string conversion
Won't be noticeable in most UI situations, but can be if used in renderings or long lists/trees
<FlatButton style={{fontSize: 12}}>
Help
</FlatButton>
<FlatButton>
Help
</FlatButton>
<HelpButton />
Avoid custom/adhoc styles, that won't scale:
Make a component for it! #DesignSystem
Storybook
React Styleguidist
Flow
Typescript
Types shine when you or someone else is refactoring some parts of the app
... all of these which becomes really useful when you're back on some module you (or someone else) wrote a few months ago
GUI (windows, dialogs...)
Filesystem
React.js powered interface
Node.js/browser adapters
X-platform toolkit (wxWidgets)
GDevelop JS codebase
GDevelop C++ codebase
Core classes (game, scene, object, textures...)
Game exporter (JS "transpiler")
libGDevelop.js
GDevelop
Electron
Web app
Embed in a native app
Squoosh.app
Spotify
Steam Chat
export default class ElectronMenuImplementation {
buildFromTemplate(template) {
this.menuTemplate = template;
return undefined;
}
showMenu(dimensions) {
if (!electron) return;
const { Menu } = electron.remote;
const browserWindow = electron.remote.getCurrentWindow();
this.menu = Menu.buildFromTemplate(this.menuTemplate);
this.menu.popup({
window: browserWindow,
x: Math.round(dimensions.left),
y: Math.round(dimensions.top + dimensions.height),
async: true, // Ensure the UI is not blocked on macOS.
});
}
}
class MaterialUIContextMenu extends React.Component { ... }
class ElectronContextMenu extends React.Component { ... }
export default class MaterialUIMenuImplementation {
buildFromTemplate(template) {
return template.map((item, id) => {
if (item.type === 'separator') {
return <Divider key={'separator' + id} />;
} else if (item.type === 'checkbox') {
return (
<MenuItem
...
/>
} else {
return (
<MenuItem
...
/>
}
}
showMenu() { ... }
}
GUI (windows, dialogs...)
Filesystem
React.js powered interface
Node.js/browser adapters
X-platform toolkit (wxWidgets)
GDevelop JS codebase
GDevelop C++ codebase
Core classes (game, scene, object, textures...)
Game exporter (JS "transpiler")
libGDevelop.js
Wow, what a difference a year can makeย
but I have to admit, once you worked a bit with it, it could be really more productive on many aspects than GD4
Amazing how much easier and streamlined the engine has become
The native web appย is better than the native app