Many PDF documents contain colorful charts, presentations, marketing materials, scanned pages, or graphics that aren't always ideal for printing or archiving.

In some cases, converting a document to grayscale reduces distractions, creates printer-friendly versions, lowers printing costs, or prepares files for black-and-white publishing.

A PDF to Grayscale Converter automates this process. Instead of editing every page manually, users can upload a PDF, choose how the grayscale conversion should be applied, preview the results, and download a newly generated document, all from within the browser.

In this tutorial, you'll build a browser-based PDF to Grayscale Converter using JavaScript. Users will be able to upload a PDF, preview every page, adjust the grayscale intensity, choose between multiple conversion modes, select which pages to process, generate a grayscale PDF, preview the final result, rename the output file, and download it without uploading their document to an external server.

We'll use PDF.js to render PDF pages, the HTML Canvas API to manipulate image pixels, and PDF-lib to generate the final downloadable PDF.

By the end of this tutorial, you'll have a complete client-side PDF processing application similar to the one available on All In One Tools.

Table of Contents

What This PDF to Grayscale Converter Does and How It Works

A PDF to Grayscale Converter transforms colorful PDF pages into shades of gray while preserving the document's layout, page dimensions, text placement, and images. Instead of removing content, it recalculates the color of every pixel so the entire page appears in grayscale.

This is useful for creating printer-friendly documents, reducing color distractions, preparing files for monochrome printing, improving consistency across scanned documents, or producing black-and-white versions for review and archival purposes.

In this project, users can upload a PDF, browse through every page, adjust the grayscale intensity, choose between different conversion modes, decide whether all pages or only selected pages should be converted, generate a new grayscale PDF, preview the completed document, rename the output file, and download it directly from the browser.

Behind the scenes, PDF.js renders each PDF page onto an HTML canvas. Once the page has been rendered, JavaScript reads the RGB values for every pixel and calculates a grayscale value using a luminance formula. The updated pixels are written back to the canvas before PDF-lib assembles all processed pages into a brand-new PDF.

A typical pixel contains four values:

const pixel = { red: 180, green: 95, blue: 40, alpha: 255 };
To convert that pixel into grayscale, JavaScript calculates a single luminance value and applies it equally to the red, green, and blue channels.

const gray = 0.299 * red + 0.587 * green + 0.114 * blue;
The resulting pixel becomes:

pixel.red = gray; pixel.green = gray; pixel.blue = gray;
Repeating this process for every pixel on every selected page creates a new grayscale version of the original PDF while preserving the overall structure of the document.

Project Setup

Before writing the conversion logic, let's create a simple project structure.

We'll build everything using HTML, CSS, and JavaScript, together with PDF.js, the Canvas API, and PDF-lib.

Our project structure looks like this:

pdf-to-grayscale/ │── index.html │── style.css │── script.js │── pdf.worker.min.js │── assets/
Keeping the HTML, styling, and JavaScript separate makes the project easier to maintain as more PDF features are added.

Libraries Used

The PDF to Grayscale Converter relies on three browser technologies that work together to render PDF pages, process image pixels, and generate a new downloadable document.

PDF.js renders PDF pages directly inside the browser.

The HTML Canvas API provides access to every pixel so JavaScript can convert colors into grayscale.

PDF-lib creates the final PDF after all selected pages have been processed.

Include the required libraries before loading your application.

```

```
Configure the PDF.js worker.

pdfjsLib.GlobalWorkerOptions.workerSrc = "pdf.worker.min.js";
Using a worker allows PDF rendering to happen in the background without freezing the browser interface.

Creating the HTML Layout

The application is divided into four main sections:

  • Upload area
  • PDF preview
  • Conversion settings
  • Download section

Create the basic page structure.

```

```
Only the upload area is visible when the page first loads. The remaining sections appear after a PDF has been successfully opened.

Selecting the Main Elements

Store references to the elements that will be used throughout the application.

const uploadSection = document.getElementById("uploadSection"); const previewSection = document.getElementById("previewSection"); const settingsSection = document.getElementById("settingsSection"); const pdfCanvas = document.getElementById("pdfCanvas");
Using these references makes it easier to update the interface as users move through the conversion process.

Uploading and Previewing PDFs

The upload area accepts both drag-and-drop and manual file selection.

When a file is selected, first verify that it's a PDF.

async function uploadPdf(file) { if (!file || file.type !== "application/pdf") { alert("Please select a PDF file."); return; } await loadPdf(file); }
Once validation succeeds, the document is loaded into memory for processing.

Loading the PDF

Convert the uploaded file into an ArrayBuffer before opening it with PDF.js.

async function loadPdf(file) { const bytes = await file.arrayBuffer(); pdfDocument = await pdfjsLib.getDocument({ data: bytes }).promise; currentPage = 1; renderPage(currentPage); }
The loaded document is stored so every page can later be converted to grayscale.

Rendering PDF Pages

PDF.js renders one page at a time onto an HTML canvas.

Retrieve the page.

const page = await pdfDocument.getPage(currentPage);
Create the viewport.

const viewport = page.getViewport({ scale: 1.5 });
Resize the canvas.

pdfCanvas.width = viewport.width; pdfCanvas.height = viewport.height;
Render the page.

await page.render({ canvasContext: pdfCanvas.getContext("2d"), viewport }).promise;
Once rendering finishes, the selected page appears inside the preview area.

Navigating Between Pages

Most PDF documents contain multiple pages, so users need simple navigation controls.

Track the current page.

let currentPage = 1; let pdfDocument = null;
Move to the previous page.

previousButton.addEventListener("click", async () => { if (currentPage > 1) { currentPage--; await renderPage(currentPage); } });
Move to the next page.

nextButton.addEventListener("click", async () => { if (currentPage < pdfDocument.numPages) { currentPage++; await renderPage(currentPage); } });
Update the page indicator.

pageCounter.textContent = `Page ${currentPage} of ${pdfDocument.numPages}`;
Users can now browse through the document before deciding how the grayscale conversion should be applied.

Building the Conversion Settings

After the PDF has been loaded and previewed, users can configure how the document should be converted to grayscale. The settings panel allows users to adjust the grayscale intensity, choose a conversion mode, decide which pages should be processed, and start the conversion.

Adjusting Grayscale Intensity

The intensity slider controls how strongly the grayscale effect is applied.

Lower values retain more of the original color, while higher values produce a true grayscale appearance.

Create the slider.

<input type="range" id="grayIntensity" min="0" max="100" value="100">
Read the selected value.

const intensity = Number(document.getElementById("grayIntensity").value);
The selected intensity will later be used when calculating the final grayscale color.

Choosing the Conversion Mode

The tool provides multiple grayscale modes for different use cases.

Create the radio buttons.

<input type="radio" name="mode" value="standard" checked> Standard Grayscale <input type="radio" name="mode" value="threshold"> Black & White <input type="radio" name="mode" value="soft"> Soft Gray
Retrieve the selected mode.

const conversionMode = document.querySelector( 'input[name="mode"]:checked' ).value;
Each mode uses a different algorithm when processing the canvas pixels.

Selecting the Pages

Users can convert either the entire document or only selected pages.

Create the page selection controls.

<input type="radio" name="pages" value="all" checked> All Pages <input type="radio" name="pages" value="custom"> Specific Pages <input type="text" id="pageRange" placeholder="e.g., 1, 3-5, 10">
Read the selected option.

const applyMode = document.querySelector( 'input[name="pages"]:checked' ).value;
Retrieve the custom page range.

const pageRange = document.getElementById("pageRange").value.trim();
When All Pages is selected, every page in the PDF is processed. Otherwise, only the pages specified by the user are converted.

Converting PDF Pages to Grayscale

Once the settings have been configured, users can begin the conversion.

Create the action button.

<button id="convertPdf">Convert to Grayscale</button>
Start the conversion.

convertButton.addEventListener("click", async () => { await convertPdf(); });

Starting Over

Users can clear the current document and return the application to its initial state.

Create the reset button.

<button id="resetTool">Start Over</button>
Reset the application.

resetTool.addEventListener("click", () => { location.reload(); });
This removes the current PDF and restores the default settings so another document can be processed.

Reading Canvas Pixels

After a page has been rendered, retrieve its pixel data.

const imageData = context.getImageData( 0, 0, canvas.width, canvas.height );
The pixel information is stored in an array.

const pixels = imageData.data;
Each pixel contains four values:

  • Red
  • Green
  • Blue
  • Alpha

We'll update the RGB values while leaving the alpha channel unchanged.

Converting Colors to Grayscale

The standard grayscale algorithm calculates a luminance value using the red, green, and blue channels.

Loop through every pixel.

for (let i = 0; i < pixels.length; i += 4) { const gray = 0.299 * pixels[i] + 0.587 * pixels[i + 1] + 0.114 * pixels[i + 2]; pixels[i] = gray; pixels[i + 1] = gray; pixels[i + 2] = gray; }
This formula produces a natural-looking grayscale image because it reflects how the human eye perceives brightness.

Applying the Selected Conversion Mode

Different conversion modes use different pixel calculations.

For example, the Black & White (Threshold) mode converts each pixel into either pure black or pure white.

const threshold = 128; const color = gray >= threshold ? 255 : 0; pixels[i] = color; pixels[i + 1] = color; pixels[i + 2] = color;
The Soft Gray mode blends the original color with the grayscale value to create a less aggressive effect.

const softGray = (gray * 0.6) + (pixels[i] * 0.4); pixels[i] = softGray; pixels[i + 1] = softGray; pixels[i + 2] = softGray;
Once the selected mode has been applied, write the updated pixels back to the canvas.

context.putImageData( imageData, 0, 0 );

Processing the Selected Pages

Instead of processing the entire document every time, convert only the pages selected by the user.

Loop through the page range.

for (let page = startPage; page <= endPage; page++) { await processPage(page); }
Each processed page is temporarily stored before the final PDF is created.

Generating the Final PDF

Create a new PDF document.

const outputPdf = await PDFLib.PDFDocument.create();
Convert the processed canvas into an image.

const imageBytes = await canvasToBytes(pdfCanvas);
Embed the image into the PDF.

const image = await outputPdf.embedPng(imageBytes);
Create a new page.

const page = outputPdf.addPage([ image.width, image.height ]);
Draw the processed image.

page.drawImage(image, { x: 0, y: 0, width: image.width, height: image.height });
Repeat this process for every selected page until the new grayscale document is complete.

Saving the Generated PDF

After all pages have been processed, save the completed document.

const pdfBytes = await outputPdf.save();
Create a downloadable PDF file.

generatedPdfBlob = new Blob([pdfBytes], { type: "application/pdf" });
The grayscale PDF is now ready for preview, renaming, and downloading.

Previewing the Result

Before downloading the converted document, it's useful to let users review the final output. This allows them to verify that the selected pages have been converted correctly and that the grayscale appearance meets their expectations.

Load the generated PDF using PDF.js.

let finalPdf = null; async function showResult() { const bytes = await generatedPdfBlob.arrayBuffer(); finalPdf = await pdfjsLib.getDocument({ data: bytes }).promise; renderResultPage(1); }
Render the selected page.

async function renderResultPage(pageNumber) { const page = await finalPdf.getPage(pageNumber); const viewport = page.getViewport({ scale: 1.5 }); resultCanvas.width = viewport.width; resultCanvas.height = viewport.height; await page.render({ canvasContext: resultCanvas.getContext("2d"), viewport }).promise; }
Users can browse through every converted page before downloading the PDF.

Renaming and Downloading

Before saving the converted PDF, users can provide a custom filename.

Create the filename input.

```
<input
type="text"
id="outputFilename"
value="grayscale-document.pdf"

```
Retrieve the filename.

function getFilename() { let filename = outputFilename.value.trim(); if (!filename) { filename = "grayscale-document.pdf"; } if (!filename.toLowerCase().endsWith(".pdf")) { filename += ".pdf"; } return filename; }
Display information about the generated PDF.

pageCount.textContent = `${finalPdf.numPages} Pages`; fileSize.textContent = formatFileSize(generatedPdfBlob.size);
Download the completed PDF.

downloadButton.addEventListener("click", () => { const url = URL.createObjectURL(generatedPdfBlob); const link = document.createElement("a"); link.href = url; link.download = getFilename(); link.click(); URL.revokeObjectURL(url); });
Everything happens locally inside the browser, allowing users to keep their documents private throughout the conversion process.

Demo: How the PDF to Grayscale Converter Works

Let's walk through the complete workflow.

Step 1: Upload the PDF

Users begin by dragging a PDF into the upload area or clicking Select PDF.

Step 2: Preview the Document

PDF.js renders the uploaded document page by page, allowing users to review the file before conversion.

Step 3: Configure the Conversion

Users adjust the grayscale intensity, choose a conversion mode, and decide whether to process all pages or only selected pages.

Step 4: Convert the PDF

Click Convert to Grayscale to begin processing the selected pages.

Step 5: Review the Converted Document

After processing is complete, the application displays a preview of the generated grayscale PDF.

Step 6: Rename and Download

Finally, users can rename the output file, review the page count and file size, and download the converted PDF.

Performance Tips

Large PDF files require more processing time because every page must be rendered and converted. Processing only the selected pages helps improve performance.

for (let page = startPage; page <= endPage; page++) { await processPage(page); }
After downloading the file, release temporary resources to reduce memory usage.

URL.revokeObjectURL(downloadUrl);
These simple optimizations help keep the converter responsive when working with large multi-page PDF documents.

Common Mistakes

One common mistake is converting the same page multiple times without first rendering the original page again. Always start with the original PDF page before applying another grayscale conversion.

await renderPage(currentPage);
Another issue is allowing users to specify invalid page numbers.

if (pageNumber < 1 || pageNumber > pdfDocument.numPages) { return; }
Finally, remember that higher output quality usually produces larger PDF files. Choosing the appropriate quality setting helps balance image clarity and file size.

Conclusion

In this tutorial, you built a browser-based PDF to Grayscale Converter using JavaScript.

You learned how to upload and preview PDF documents, render pages with PDF.js, convert colorful pages into grayscale using the Canvas API, process selected pages, generate a new PDF with PDF-lib, preview the completed document, rename the output file, and download it directly from the browser.

Because all processing happens locally, users can convert PDF documents to grayscale without uploading sensitive files to an external server.

You can explore the complete workflow using the PDF to Grayscale Converter.

From here, you can extend the project with additional features such as sepia conversion, brightness and contrast controls, custom grayscale presets, batch PDF processing, or support for additional image filters.