How to Create a FrontPanel Application Project
This guide will show how to setup a project for a FrontPanel application. The application is created using Webpack, Typescript and the React user interface development library.
Install Prerequisites
- Node.js – Download | Node.js (nodejs.org)
- Node Package Manager – Downloading and installing Node.js and npm | npm Docs (npmjs.com)
- Source Code Editor
- FrontPanel Application version 6.0.0 or greater
Create the Application Project
Create a directory for the application project and set it to be the current directory.
mkdir frontpanel-app
cd frontpanel-app
Code language: Bash (bash)
Create the package.json
file using npm.
npm init
Code language: Bash (bash)
This command should create a package.json
file in the ‘frontpanel-alloy-app’ directory.
Setup Typescript
Install Typescript development dependencies.
npm install --save-dev typescript ts-node @types/node
Code language: Bash (bash)
Create a tsconfig.json
file in the project directory and add the following content.
{
/* Visit https://aka.ms/tsconfig to read more about this file */
"compilerOptions": {
"target": "es6",
"lib": [
"dom",
"dom.iterable",
"es2023"
],
"allowJs": true,
"skipLibCheck": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"strict": true,
"forceConsistentCasingInFileNames": true,
"noFallthroughCasesInSwitch": true,
"module": "esnext",
"moduleResolution": "node",
"resolveJsonModule": true,
"isolatedModules": true,
"noImplicitAny": true,
"noImplicitThis": true,
"noUnusedLocals": true,
"jsx": "react-jsx",
"sourceMap": true,
},
"include": [
"src"
]
}
Code language: JSON / JSON with Comments (json)
Setup Webpack Bundler
Install Webpack development dependencies.
npm install --save-dev webpack webpack-cli webpack-dev-server style-loader css-loader ts-loader html-webpack-plugin copy-webpack-plugin webpack-merge
Code language: Bash (bash)
Create webpack.common.js
file in the project directory and add the following content.
const path = require('path');
const HtmlWebpackPlugin = require('html-webpack-plugin');
module.exports = {
entry: './src/index.tsx',
plugins: [
new HtmlWebpackPlugin({
title: 'FrontPanel Application',
template: path.resolve(__dirname, 'src/index.html'),
}),
],
module: {
rules: [
{
test: /\.tsx?$/,
use: 'ts-loader',
exclude: /node_modules/,
},
{
test: /\.css$/i,
use: ['style-loader', 'css-loader'],
},
{
test: /\.(ico)$/i,
type: 'asset/resource',
generator: {
filename: 'assets/icons/[name][ext]'
}
},
{
test: /\.(png|svg|jpg|jpeg|gif)$/i,
type: 'asset/resource',
generator: {
filename: 'assets/images/[name][ext]'
}
},
{
test: /\.(woff|woff2|eot|ttf|otf)$/i,
type: 'asset/resource',
generator: {
filename: 'assets/fonts/[name][ext]'
}
},
{
test: /\.(bit)$/i,
type: 'asset/resource',
generator: {
filename: 'assets/bitfiles/[name][ext]'
}
},
],
},
resolve: {
extensions: ['.tsx', '.ts', '.js'],
modules: [path.resolve(__dirname, 'src'), path.resolve(__dirname, 'assets'), 'node_modules'],
},
output: {
filename: '[name].bundle.js',
path: path.resolve(__dirname, 'dist'),
clean: true,
},
};
Code language: JavaScript (javascript)
Create webpack.dev.js
file in the project directory and add the following content.
const { merge } = require('webpack-merge');
const common = require('./webpack.common.js');
module.exports = merge(common, {
mode: 'development',
devtool: 'inline-source-map',
devServer: {
static: './dist',
},
});
Code language: JavaScript (javascript)
Create webpack.prod.js
file in the project directory and add the following content.
const { merge } = require('webpack-merge');
const common = require('./webpack.common.js');
module.exports = merge(common, {
mode: 'production',
});
Code language: JavaScript (javascript)
Edit the package.json
file in the project directory so that the content of the “scripts” section is as follows.
"scripts": {
"build:dev": "webpack --config webpack.dev.js",
"build": "webpack --config webpack.prod.js",
"start": "webpack serve --config webpack.dev.js",
"test": "echo \"Error: no test specified\" && exit 1"
},
Code language: JSON / JSON with Comments (json)
Setup React
Install React dependencies.
npm install react@^18.3.1 react-dom@^18.3.1
Code language: Bash (bash)
Install React development dependencies.
npm install --save-dev @types/react@^18.3.12 @types/react-dom@^18.3.1
Code language: Bash (bash)
Setup Asar
Install Asar development dependencies.
npm install --save-dev --engine-strict @electron/asar@^3.3.0
Code language: Bash (bash)
Edit the package.json
file in the project directory to add “pack:dev” and “pack” elements to the “scripts” section.
"scripts": {
"pack:dev": "webpack --config webpack.dev.js && asar pack ./dist ./output/app-dev.asar",
"pack": "webpack --config webpack.prod.js && asar pack ./dist ./output/app.asar",
"build:dev": "webpack --config webpack.dev.js",
"build": "webpack --config webpack.prod.js",
"start": "webpack serve --config webpack.dev.js",
"test": "echo \"Error: no test specified\" && exit 1"
},
Code language: JSON / JSON with Comments (json)
Setup FrontPanel
Install FrontPanel React Components library dependency.
npm install @opalkelly/frontpanel-react-components@^0.4.0
Code language: Bash (bash)
Add Application Sources
Create a src
directory within the application project directory and add the following files.
src/index.css
body {
margin: 0;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", "Ubuntu",
"Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
code {
font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", monospace;
}
Code language: CSS (css)
src/index.tsx
import React from "react";
import ReactDOM from "react-dom/client";
import "./index.css";
//TODO: Replace 'frontpanel.bit' with the name of the configuration file.
//import "../assets/frontpanel.bit";
import App from "./App";
const root = ReactDOM.createRoot(document.getElementById("root") as HTMLElement);
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);
Code language: TypeScript (typescript)
src/index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="<%=require("../assets/favicon.ico")%>" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta
name="description"
content="FrontPanel Alloy Application"
/>
<title>FrontPanel Alloy Application</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
</body>
</html>
Code language: HTML, XML (xml)
src/App.css
.App {
text-align: center;
background-color: #282c34;
min-height: 100vh;
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
font-size: calc(10px + 2vmin);
color: white;
}
Code language: CSS (css)
src/App.tsx
import React from "react";
import "./App.css";
import FrontPanel from "./FrontPanel";
import {
IDevice,
IFrontPanel,
WorkQueue,
DataProgressCallback,
ByteCount
} from "@opalkelly/frontpanel-alloy-core";
import { Application } from "@opalkelly/frontpanel-react-components";
import FrontPanelLogo from "../assets/logo512.png";
/**
* Loads the specified configuration file into the FPGA on the specified device.
* @param filename The name of the configuration file to load.
* @param device The device to load the configuration file into.
*/
const loadConfiguration = async (filename: string, device: IDevice): Promise<void> => {
console.log("Loading Configuration File:", filename);
try {
const response = await fetch("frontpanel://localhost/assets/bitfiles/" + filename);
if (!response.ok) {
throw new Error("Network response was not ok");
}
const arrayBuffer = await response.arrayBuffer();
const reportProgress: DataProgressCallback = (total: ByteCount, completed: ByteCount) => {
console.log("Configuration Progress: ", completed, " of ", total);
};
await device
.getFPGA()
.loadConfiguration(arrayBuffer, arrayBuffer.byteLength, reportProgress);
console.log("Load Configuration Complete");
} catch (error) {
console.error("Failed to load configuration file:", filename, "\n", error);
}
};
/**
* Initializes a device by opening the specified device and
* configuring the device with specified configuration file.
* @param serialNumber The serial number of the device to open.
* @param configurationFilename The name of the configuration file to load.
* @returns {Promise<IDevice>} A promise that resolves to the opened device.
*/
const initializeDevice = async (
serialNumber: string,
configurationFilename: string
): Promise<IDevice> => {
console.log("Opening Device...");
const device = await window.FrontPanelAPI.deviceManager.openDevice(serialNumber);
const deviceInfo = await device.getInfo();
console.log("Opened Device:", deviceInfo.productName, " SerialNumber:", deviceInfo.serialNumber);
await loadConfiguration(configurationFilename, device);
return device;
};
const DeviceWorkQueue = new WorkQueue();
function App() {
const [frontpanel, setFrontPanel] = React.useState<IFrontPanel>();
React.useEffect(() => {
let device: IDevice;
DeviceWorkQueue.post(async () => {
//TODO: Replace 'frontpanel.bit' with the name of the configuration file to load.
device = await initializeDevice("", "frontpanel.bit");
const frontpanel = await device.getFPGA().getFrontPanel();
setFrontPanel(frontpanel);
}).catch((error) => {
device?.close();
console.error("Failed to initialize the application:", error);
});
return () => {
device?.close();
};
}, []);
if (frontpanel !== undefined) {
return (
<div className="RootPanel">
<Application>
<FrontPanel
name="Counters"
frontpanel={frontpanel}
workQueue={DeviceWorkQueue}
/>
</Application>
</div>
);
} else {
return (
<div className="RootPanel">
<img src={FrontPanelLogo} />
</div>
);
}
}
export default App;
Code language: JavaScript (javascript)
src/FrontPanel.css
.ControlPanel {
display: flex;
flex-direction: row;
align-content: stretch;
background-color: rgb(255, 255, 255);
padding: 10px;
margin: 10px;
border-radius: 8px;
}
Code language: CSS (css)
src/FrontPanel.tsx
import { Component } from "react";
import {
IFrontPanel,
WorkQueue
} from "@opalkelly/frontpanel-alloy-core";
import "./FrontPanel.css";
export interface FrontPanelProps {
name: string;
frontpanel: IFrontPanel;
workQueue: WorkQueue;
}
class FrontPanel extends Component<FrontPanelProps> {
// Constructor
constructor(props: FrontPanelProps) {
super(props);
}
// Component Lifecycle Methods
componentDidMount() {
}
componentWillUnmount() {
}
render() {
return (
<div>TODO: Add JSX here</div>
);
}
}
export default FrontPanel;
Code language: JavaScript (javascript)
src/declarations.d.ts
declare module "*.png" {
const value: string;
export default value;
}
Code language: TypeScript (typescript)
Add Application Assets
Create a assets
directory within the application project directory.
Download the following image and place it in the assets
directory.

Build and Package the Application
Create the production application package by running.
npm run pack
Code language: Bash (bash)
Create the development application package by running:
npm run pack:dev
Code language: Bash (bash)
Run the Application
Launch the FrontPanel Platform application and select the output/app.asar
or output/app-dev.asar
file.