Hello everyone!
This March, I experienced a career transition – I was laid off by my company. After working for so long, I felt quite exhausted and had also been contemplating a career shift. It was time to create something of my own.
Although I felt momentarily lost and unsure where to start, I decided to begin with a simple tool website. By summarizing my daily work needs for tools, I found that the most frequent scenarios involved image compression and format conversion. And so, a picture converter project was born!
Limgx:My Image Conversion Marvel
I named this product Limgx.
- L stands for local (local processing)
- img represents image
- X symbolizes the conversion function
A simple, straightforward, and powerful name!
Welcome to visit my work: https://limgx.com
Rolling Up Sleeves to Code!
Implementation Plan: The Pleasant Surprise of FFmpeg.WASM
When brainstorming the implementation, I initially considered traditional front-end Canvas local processing or building a server for conversions. However, during my research, I unexpectedly discovered an exciting piece of cutting-edge technology: FFmpeg actually has a WASM version! https://ffmpegwasm.netlify.app/
Although the WASM version may not match the performance of the local native FFmpeg, it is definitely much faster than Canvas conversion! The only „drawback“ is that the WASM package size reaches 30MB. Nevertheless, through careful optimization of the user experience and browser caching mechanisms, I believe this is not a major issue.
Using FFmpeg.wasm is incredibly intuitive, as simple as piecing together parameters in the command line:
JavaScript
const transcode = async () => {
const ffmpeg = ffmpegRef.current;
// Write the input file
await ffmpeg.writeFile('input.webm', await fetchFile('https://raw.githubusercontent.com/ffmpegwasm/testdata/master/Big_Buck_Bunny_180_10s.webm'));
// Execute the conversion command
await ffmpeg.exec(['-i', 'input.webm', 'output.mp4']);
// Read the output result
const data = await ffmpeg.readFile('output.mp4');
}
To allow users to flexibly choose input and output quality, we need to dynamically pass these parameters. Since different image formats require different codecs and parameters, to keep the code clean and maintainable, I adopted the strategy pattern. Here’s an example of a strategy for handling WebP conversion:
JavaScript
/**
* WebP Conversion Strategy
* Handles image conversion and compression for the WebP format
*/
export class WebPConversionStrategy implements ConversionStrategy {
getArgs(inputFileName: string, outputName: string, quality: number, width?: number, height?: number): string[] {
const args = ['-i', inputFileName];
// Resolution scaling
if (width || height) {
let scaleArg = 'scale=';
scaleArg += width ? `${width}:` : '-1:';
scaleArg += height ? `${height}` : '-1';
args.push('-vf', scaleArg);
}
// Explicitly specify the use of the libwebp encoder
args.push('-c:v', 'libwebp');
// WebP quality control (directly using a quality value between 0-100)
args.push('-quality', String(quality));
// Ensure lossy compression is used
args.push('-lossless', '0');
// Use the best compression method
args.push('-method', '6');
args.push(outputName);
return args;
}
}
FFmpeg’s power extends far beyond image conversion. It can easily be expanded to more interesting application scenarios, such as WebP or GIF animated image generators.
Technology Stack Selection and Considerations
After finalizing the plan, I began selecting the appropriate technology stack. Eventually, I chose a combination of Next.js + Tailwind CSS + Shadcn UI + next-intl + ffmpeg.wasm.
For the deployment platform, I selected Cloudflare. Although Next.js pairs more „officially“ with Vercel, Cloudflare’s unlimited free traffic was extremely appealing to me, and it also integrates domain management and other functions, allowing me to unify my operations and maintenance work on the Cloudflare platform.
SEO: The Lifeline of Website Traffic
For a website built by an individual, a large portion of traffic comes from SEO (Search Engine Optimization), making it a top priority.
- Server-Side Rendering (SSR): For SEO considerations, my framework had to support SSR, so I chose Next.js. However, if I were to start over now, I might lean more toward SolidStart. The reason is simple: my website has minimal dependency on the React ecosystem, and Solid.js pages may outperform React pages in performance metrics (such as TTFB and TTI) in some cases, which could help me achieve higher SEO rankings.
- Internationalization (i18n): I used the next-intl library to implement internationalization. It supports i18n routing and can dynamically generate translated metadata based on language, which is very friendly to global users and search engines.
Style and Design: Balancing Aesthetics and Efficiency
I chose a combination of Shadcn UI and Tailwind CSS. Shadcn UI provides a unique design style and reusable components while allowing me to deeply customize them as needed.
For the overall website design, I experimented with AI assistance. Although the results were sometimes random, I personally think the final effect turned out quite good!
Summary
Currently, website traffic is low, and it is speculated that this may be because Google has not yet crawled the new content. After submitting the Sitemap for a new website, it takes time to take effect, so I plan to observe for one month first.
May I ask if you all have any optimization suggestions?