diff --git a/src/ImageSharp/Formats/Ani/AniDecoder.cs b/src/ImageSharp/Formats/Ani/AniDecoder.cs
new file mode 100644
index 0000000000..9794867feb
--- /dev/null
+++ b/src/ImageSharp/Formats/Ani/AniDecoder.cs
@@ -0,0 +1,35 @@
+// Copyright (c) Six Labors.
+// Licensed under the Six Labors Split License.
+using SixLabors.ImageSharp.PixelFormats;
+
+namespace SixLabors.ImageSharp.Formats.Ani;
+
+internal sealed class AniDecoder : ImageDecoder
+{
+ private AniDecoder()
+ {
+ }
+
+ ///
+ /// Gets the shared instance.
+ ///
+ public static AniDecoder Instance { get; } = new();
+
+ protected override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
+ {
+ Guard.NotNull(options, nameof(options));
+ Guard.NotNull(stream, nameof(stream));
+ Image image = new AniDecoderCore(options).Decode(options.Configuration, stream, cancellationToken);
+ ScaleToTargetSize(options, image);
+ return image;
+ }
+
+ protected override Image Decode(DecoderOptions options, Stream stream, CancellationToken cancellationToken) => this.Decode(options, stream, cancellationToken);
+
+ protected override ImageInfo Identify(DecoderOptions options, Stream stream, CancellationToken cancellationToken)
+ {
+ Guard.NotNull(options, nameof(options));
+ Guard.NotNull(stream, nameof(stream));
+ return new AniDecoderCore(options).Identify(options.Configuration, stream, cancellationToken);
+ }
+}
diff --git a/src/ImageSharp/Formats/Ani/AniDecoderCore.cs b/src/ImageSharp/Formats/Ani/AniDecoderCore.cs
new file mode 100644
index 0000000000..c60036c465
--- /dev/null
+++ b/src/ImageSharp/Formats/Ani/AniDecoderCore.cs
@@ -0,0 +1,35 @@
+// Copyright (c) Six Labors.
+// Licensed under the Six Labors Split License.
+
+using SixLabors.ImageSharp.Formats.Icon;
+using SixLabors.ImageSharp.IO;
+
+namespace SixLabors.ImageSharp.Formats.Ani;
+
+internal class AniDecoderCore : ImageDecoderCore
+{
+ public AniDecoderCore(DecoderOptions options)
+ : base(options)
+ {
+ }
+
+ protected override Image Decode(BufferedReadStream stream, CancellationToken cancellationToken)
+ {
+ this.ReadHeader(stream);
+ throw new NotImplementedException();
+ }
+
+ protected override ImageInfo Identify(BufferedReadStream stream, CancellationToken cancellationToken)
+ {
+ throw new NotImplementedException();
+ }
+
+ private void ReadHeader(Stream stream)
+ {
+ // Skip the identifier
+ stream.Skip(12);
+ Span buffer = stackalloc byte[AniHeader.Size];
+ _ = stream.Read(buffer);
+ AniHeader header = AniHeader.Parse(buffer);
+ }
+}
diff --git a/src/ImageSharp/Formats/Ani/AniHeader.cs b/src/ImageSharp/Formats/Ani/AniHeader.cs
new file mode 100644
index 0000000000..8f985c4c6e
--- /dev/null
+++ b/src/ImageSharp/Formats/Ani/AniHeader.cs
@@ -0,0 +1,34 @@
+// Copyright (c) Six Labors.
+// Licensed under the Six Labors Split License.
+
+using System.Runtime.InteropServices;
+
+namespace SixLabors.ImageSharp.Formats.Ani;
+
+[StructLayout(LayoutKind.Sequential, Pack = 1, Size = Size)]
+internal readonly struct AniHeader
+{
+ public const int Size = 36;
+
+ public ushort Frames { get; }
+
+ public ushort Steps { get; }
+
+ public ushort Width { get; }
+
+ public ushort Height { get; }
+
+ public ushort BitCount { get; }
+
+ public ushort Planes { get; }
+
+ public ushort DisplayRate { get; }
+
+ public ushort Flags { get; }
+
+ public static AniHeader Parse(in ReadOnlySpan data)
+ => MemoryMarshal.Cast(data)[0];
+
+ public readonly unsafe void WriteTo(in Stream stream)
+ => stream.Write(MemoryMarshal.Cast([this]));
+}
diff --git a/src/ImageSharp/Formats/Ani/AniMetadata.cs b/src/ImageSharp/Formats/Ani/AniMetadata.cs
new file mode 100644
index 0000000000..ce862b5eef
--- /dev/null
+++ b/src/ImageSharp/Formats/Ani/AniMetadata.cs
@@ -0,0 +1,23 @@
+// Copyright (c) Six Labors.
+// Licensed under the Six Labors Split License.
+
+using SixLabors.ImageSharp.PixelFormats;
+
+namespace SixLabors.ImageSharp.Formats.Ani;
+
+internal class AniMetadata : IFormatMetadata
+{
+
+ public static AniMetadata FromFormatConnectingMetadata(FormatConnectingMetadata metadata) => throw new NotImplementedException();
+
+ public void AfterImageApply(Image destination)
+ where TPixel : unmanaged, IPixel => throw new NotImplementedException();
+
+ public IDeepCloneable DeepClone() => throw new NotImplementedException();
+
+ public PixelTypeInfo GetPixelTypeInfo() => throw new NotImplementedException();
+
+ public FormatConnectingMetadata ToFormatConnectingMetadata() => throw new NotImplementedException();
+
+ AniMetadata IDeepCloneable.DeepClone() => throw new NotImplementedException();
+}