diff --git a/blocks/duotone-filter/duotone.php b/blocks/duotone-filter/duotone.php
new file mode 100644
index 00000000..68390c77
--- /dev/null
+++ b/blocks/duotone-filter/duotone.php
@@ -0,0 +1,60 @@
+ $r / 0xff,
+ 'g' => $g / 0xff,
+ 'b' => $b / 0xff,
+ );
+ }
+}
+
+// phpcs:disable
+$duotone_id = $block['attrs']['duotoneId'];
+$duotone_dark = hex2rgb( $block['attrs']['duotoneDark'] );
+$duotone_light = hex2rgb( $block['attrs']['duotoneLight'] );
+// phpcs:enable
+
+?>
+
+
diff --git a/blocks/duotone-filter/index.php b/blocks/duotone-filter/index.php
new file mode 100644
index 00000000..fe5dcd7c
--- /dev/null
+++ b/blocks/duotone-filter/index.php
@@ -0,0 +1,31 @@
+ 'block-experiments',
+ 'style' => 'block-experiments',
+ 'editor_style' => 'block-experiments-editor',
+ ] );
+
+ function is_supported_block( $block ) {
+ $supported_blocks = [ 'core/image', 'core/cover' ];
+ return in_array( $block['blockName'], $supported_blocks, true );
+ }
+
+ add_filter( 'render_block', function ( $block_content, $block ) {
+ if (
+ ! is_supported_block( $block ) ||
+ ! array_key_exists( 'duotoneId', $block['attrs'] ) ||
+ ! array_key_exists( 'duotoneDark', $block['attrs'] ) ||
+ ! array_key_exists( 'duotoneLight', $block['attrs'] )
+ ) {
+ return $block_content;
+ }
+
+ ob_start();
+ include 'duotone.php';
+ $duotone = ob_get_clean();
+
+ return $duotone . $block_content;
+ }, 10, 2 );
+} );
diff --git a/blocks/duotone-filter/src/duotone.js b/blocks/duotone-filter/src/duotone.js
new file mode 100644
index 00000000..7688acfb
--- /dev/null
+++ b/blocks/duotone-filter/src/duotone.js
@@ -0,0 +1,79 @@
+/**
+ * WordPress dependencies
+ */
+import { SVG } from '@wordpress/components';
+
+const hex2rgb = ( color = '' ) => {
+ let r = '0';
+ let g = '0';
+ let b = '0';
+
+ if ( color.length === 4 ) {
+ r = '0x' + color[ 1 ] + color[ 1 ];
+ g = '0x' + color[ 2 ] + color[ 2 ];
+ b = '0x' + color[ 3 ] + color[ 3 ];
+ } else if ( color.length === 7 ) {
+ r = '0x' + color[ 1 ] + color[ 2 ];
+ g = '0x' + color[ 3 ] + color[ 4 ];
+ b = '0x' + color[ 5 ] + color[ 6 ];
+ } else {
+ return {};
+ }
+
+ return {
+ r: r / 0xff,
+ g: g / 0xff,
+ b: b / 0xff,
+ };
+};
+
+function Duotone( { id: duotoneId, darkColor, lightColor } ) {
+ const duotoneDark = hex2rgb( darkColor );
+ const duotoneLight = hex2rgb( lightColor );
+
+ return (
+
+ );
+}
+
+export default Duotone;
diff --git a/blocks/duotone-filter/src/index.js b/blocks/duotone-filter/src/index.js
new file mode 100644
index 00000000..b0b432e5
--- /dev/null
+++ b/blocks/duotone-filter/src/index.js
@@ -0,0 +1,132 @@
+/**
+ * WordPress dependencies
+ */
+import { InspectorControls, PanelColorSettings } from '@wordpress/block-editor';
+import { createHigherOrderComponent, useInstanceId } from '@wordpress/compose';
+import { useEffect } from '@wordpress/element';
+import { addFilter } from '@wordpress/hooks';
+import { __ } from '@wordpress/i18n';
+
+/**
+ * Internal dependencies
+ */
+import Duotone from './duotone';
+
+const SUPPORTED_BLOCKS = [ 'core/image', 'core/cover' ];
+
+export const isSupportedBlock = ( blockName ) =>
+ SUPPORTED_BLOCKS.includes( blockName );
+
+const withDuotoneAttributes = ( settings, blockName ) => {
+ if ( isSupportedBlock( blockName ) ) {
+ Object.assign( settings.attributes, {
+ duotoneId: {
+ type: 'string',
+ },
+ duotoneDark: {
+ type: 'string',
+ },
+ duotoneLight: {
+ type: 'string',
+ },
+ } );
+ }
+ return settings;
+};
+
+const withDuotoneEditorControls = createHigherOrderComponent(
+ ( BlockEdit ) => ( props ) => {
+ const { name: blockName, attributes, setAttributes } = props;
+
+ if ( ! isSupportedBlock( blockName ) ) {
+ return