Edit in JSFiddle

const iframe = document.createElement('iframe');

// Make sure to initialize it, before you load HexEd.it into the IFrame, otherwise you're not able to catch startup-errors.
const hexedit = HexEdit(iframe);

// We are using ""#defer", to make HexEd.it wait, till you've prepared the interface.
iframe.setAttribute('src', 'https://hexed.it#defer');

document.body.appendChild(iframe);

document.getElementById('file').addEventListener('change', async (event) => {
	await hexedit.request('editors.add', { files: event.target.files });

	await hexedit.request('ui.components', { visible: [] });
	
	await hexedit.subscribe('cursorChange', (data) => {
		console.log(data);
	});

	await hexedit.request('launch');

	try {
		await hexedit.request('launch');
	} catch (error) {
		if (error.code === 'ALREADY_LAUNCHED') {
			console.log(`I've got "${error.message}" and that's absolutely right, because calling the "launch" action twice makes no sense!`);
		}
	}

});

function HexEdit (receiver) {
	const subscriptions = {};
	const handlers = {};
	let messageId = 0;

	function request (action, params) {
		return new Promise((resolve, reject) => {
			messageId++;

			/**
			 * IMPORTANT: We need to create a new file, from the existing File instance, otherwise Google Chrome is not able to send it to HexEd.it.
			 * On the other hand, Microsoft Edge cannot create new files by "new File()". So, we catch the error in Edge and return the original file.
			 */
			if (action === 'editors.add' && params.files) {
				params.files = [...params.files].map((file) => {
					if (file.constructor === Blob) {
						return file;
					}

					try {
						return new File([file], file.name);
					}
					catch (ex) {
						return file;
					}
				});
			}

			receiver.contentWindow.postMessage({
				messageId,
				action,
				params
			}, 'https://hexed.it');

			handlers[messageId] = { resolve, reject };
		});
	}

	async function subscribe (eventType, callback) {
		if (!subscriptions[eventType]) {
			subscriptions[eventType] = [callback];

			try {
				await request('subscribe', {
					eventType
				});
			}
			catch (error) {
				delete subscriptions[eventType];

				throw error;
			}
		}
		else {
			subscriptions[eventType].push(callback);
		}
	}

	async function unsubscribe (eventType, callback) {
		if (subscriptions[eventType]) {
			subscriptions[eventType] = subscriptions[eventType].filter((subscriptionCallback) => subscriptionCallback !== callback);

			if (subscriptions[eventType].length === 0) {
				delete subscriptions[eventType];
			}

			await request('unsubscribe', {
				eventType
			});
		}
	}

	window.addEventListener('message', (event) => {
		if (event.data.messageId !== undefined) {
			const handler = handlers[event.data.messageId];

			if (handler) {
				delete handlers[event.data.messageId];

				if (event.data.type === 'done') {
					handler.resolve(event.data.payload);
				}
				else if (event.data.type === 'error') {
					handler.reject(event.data.payload);
				}
			}
		}
		else if (event.data.type === 'event') {
			if (subscriptions[event.data.payload.eventType]) {
				for (const callback of subscriptions[event.data.payload.eventType]) {
					callback(event.data.payload);
				}
			}
		}
	});

	return {
		request,
		subscribe,
		unsubscribe
	};
}
<h1>HexEd.it API - Promise-based API</h1>

<input type="file" id="file" />
body {
	font-family: Arial, "Helvetica Neue", Helvetica, sans-serif;
}

iframe {
	width: 100%;
	height: 500px;

	border: 0;
}